polaris-python 0.0.1.dev3__py3-none-any.whl → 0.0.1.dev4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
polaris/_version.py CHANGED
@@ -13,4 +13,4 @@
13
13
  # CONDITIONS OF ANY KIND, either express or implied. See the License for the
14
14
  # specific language governing permissions and limitations under the License.
15
15
 
16
- __version__ = "0.0.1.dev3"
16
+ __version__ = "0.0.1.dev4"
polaris/api/log.py CHANGED
@@ -97,7 +97,9 @@ def _create_file_logger(name, path, level, max_bytes, backup_count, max_age_days
97
97
  path.parent.mkdir(parents=True, exist_ok=True)
98
98
  handler = ExclusiveRotatingFileHandler(path, "a", max_bytes, backup_count,
99
99
  encoding="utf-8", max_age_days=max_age_days)
100
- handler.setFormatter(logging.Formatter("[%(asctime)s][%(levelname)s][%(process)d]: %(message)s"))
100
+ handler.setFormatter(logging.Formatter(
101
+ "[%(asctime)s][%(levelname)s][%(process)d][%(filename)s:%(lineno)d]: %(message)s"
102
+ ))
101
103
  instance = logging.Logger(name, level=level)
102
104
  instance.addHandler(handler)
103
105
  instance.propagate = False
@@ -19,12 +19,14 @@ from __future__ import annotations
19
19
 
20
20
  import asyncio
21
21
  import itertools
22
+ import logging
22
23
  import time
23
24
  import traceback
24
25
  from collections.abc import AsyncIterator, Awaitable, Callable, Sequence
25
26
  from dataclasses import dataclass
26
27
 
27
28
  import grpc
29
+ from google.protobuf.json_format import MessageToJson
28
30
  from google.protobuf.message import Message
29
31
  from polarismesh_specification.api.v1.service_manage import (
30
32
  client_pb2,
@@ -53,6 +55,7 @@ _REQUEST_ID_PREFIX = {
53
55
  "DownloadSkill": "1",
54
56
  }
55
57
  _REQUEST_ID_SEEDS = {prefix: itertools.count(1) for prefix in set(_REQUEST_ID_PREFIX.values())}
58
+ REPORT_CLIENT_LOG_INTERVAL_SECONDS = 30 * 60
56
59
 
57
60
 
58
61
  def _request_id(method: str) -> tuple[tuple[str, str], ...]:
@@ -72,6 +75,32 @@ def _response_location(response) -> str:
72
75
  return "-"
73
76
 
74
77
 
78
+ def _report_client_request_summary(request) -> str:
79
+ try:
80
+ client_type = client_pb2.Client.ClientType.Name(request.type)
81
+ except ValueError:
82
+ client_type = str(request.type)
83
+ config_metadata = request.config_metadata.value if request.HasField("config_metadata") else ""
84
+ config_enabled = request.config_enabled.value if request.HasField("config_enabled") else False
85
+ stats = [
86
+ (
87
+ f"{stat.protocol.value or '-'}://{stat.target.value or '-'}:"
88
+ f"{stat.port.value}{stat.path.value or '/'}"
89
+ )
90
+ for stat in request.stat
91
+ ]
92
+ return (
93
+ f"clientId={request.id.value} host={request.host.value} "
94
+ f"clientType={client_type} clientVersion={request.version.value} "
95
+ f"configEnabled={config_enabled} configMetadataLen={len(config_metadata)} "
96
+ f"stats={','.join(stats) or '-'}"
97
+ )
98
+
99
+
100
+ def _compact_message_json(message: Message) -> str:
101
+ return MessageToJson(message, preserving_proto_field_name=True, indent=None)
102
+
103
+
75
104
  @dataclass(frozen=True, slots=True)
76
105
  class GrpcSettings:
77
106
  """Values required by the gRPC transport."""
@@ -115,6 +144,25 @@ class GrpcConnection:
115
144
  self.address_resolver = None
116
145
  self.on_switch = None
117
146
  self._switch_task = None
147
+ self._last_report_client_log = None
148
+ self._last_report_client_log_at = None
149
+
150
+ def _should_log_report_client(self, request) -> bool:
151
+ content = request.SerializeToString(deterministic=True)
152
+ now = time.monotonic()
153
+ if (
154
+ content == self._last_report_client_log
155
+ and self._last_report_client_log_at is not None
156
+ and now - self._last_report_client_log_at < REPORT_CLIENT_LOG_INTERVAL_SECONDS
157
+ ):
158
+ return False
159
+ self._last_report_client_log = content
160
+ self._last_report_client_log_at = now
161
+ return True
162
+
163
+ def _reset_report_client_log(self) -> None:
164
+ self._last_report_client_log = None
165
+ self._last_report_client_log_at = None
118
166
 
119
167
  def _metadata(self) -> tuple[tuple[str, str], ...]:
120
168
  if not self.settings.token:
@@ -160,29 +208,68 @@ class GrpcConnection:
160
208
  address = self._addresses[self._address_index % len(self._addresses)]
161
209
  started = time.monotonic()
162
210
  response = None
211
+ request_metadata = _request_id(method) + extra_metadata + self._metadata()
212
+ request_id = next(
213
+ (value for key, value in request_metadata if key == "request-id"),
214
+ "-",
215
+ )
163
216
  try:
217
+ if method == "ReportClient" and self.base_logger.isEnabledFor(logging.DEBUG):
218
+ self.base_logger.debug(
219
+ "reportClient request address=%s requestId=%s request=%s",
220
+ address,
221
+ request_id,
222
+ _compact_message_json(request),
223
+ )
164
224
  stub = await self._rpc_stub(method, address)
165
225
  rpc: Callable[..., Awaitable[response_pb2.Response]] = getattr(stub, method)
166
226
  response = await rpc(
167
227
  request,
168
228
  timeout=self.settings.timeout_seconds if timeout_seconds is None else timeout_seconds,
169
- metadata=_request_id(method) + extra_metadata + self._metadata(),
229
+ metadata=request_metadata,
170
230
  )
171
231
  code = response_code(response)
172
232
  cost = (time.monotonic() - started) * 1000
173
233
  if method == "ReportClient":
234
+ summary = _report_client_request_summary(request)
174
235
  if code == ServerCode.ExecuteSuccess:
175
- self.base_logger.info(
176
- "reportClient success address=%s serverCode=%s cost=%.3fms location=%s",
177
- address, code, cost, _response_location(response),
178
- )
236
+ if self._should_log_report_client(request):
237
+ self.base_logger.info(
238
+ "reportClient success address=%s serverCode=%s cost=%.3fms "
239
+ "location=%s requestId=%s %s",
240
+ address,
241
+ code,
242
+ cost,
243
+ _response_location(response),
244
+ request_id,
245
+ summary,
246
+ )
179
247
  else:
248
+ self._reset_report_client_log()
180
249
  self.base_logger.warning(
181
- "reportClient failed address=%s serverCode=%s cost=%.3fms location=%s",
182
- address, code, cost, _response_location(response),
250
+ "reportClient failed address=%s serverCode=%s cost=%.3fms "
251
+ "location=%s requestId=%s %s",
252
+ address,
253
+ code,
254
+ cost,
255
+ _response_location(response),
256
+ request_id,
257
+ summary,
258
+ )
259
+ if self.base_logger.isEnabledFor(logging.DEBUG):
260
+ self.base_logger.debug(
261
+ "reportClient response address=%s requestId=%s response=%s",
262
+ address,
263
+ request_id,
264
+ _compact_message_json(response),
183
265
  )
184
- self.logger.debug("Polaris ReportClient success address=%s code=%s cost=%.3fms",
185
- address, code, cost)
266
+ self.logger.debug(
267
+ "Polaris ReportClient success address=%s code=%s requestId=%s %s",
268
+ address,
269
+ code,
270
+ request_id,
271
+ summary,
272
+ )
186
273
  else:
187
274
  # Heartbeats repeat on every TTL; keep them out of the INFO stream like polaris-go.
188
275
  quiet_methods = {"Heartbeat", "GetServiceContract"}
@@ -196,12 +283,28 @@ class GrpcConnection:
196
283
  last_error = exc
197
284
  cost = (time.monotonic() - started) * 1000
198
285
  if method == "ReportClient":
286
+ summary = _report_client_request_summary(request)
287
+ self._reset_report_client_log()
199
288
  self.base_logger.warning(
200
- "reportClient failed address=%s code=%s cost=%.3fms: %s",
201
- address, exc.code().name, cost, exc.details(),
289
+ "reportClient failed address=%s code=%s cost=%.3fms "
290
+ "requestId=%s %s: %s",
291
+ address,
292
+ exc.code().name,
293
+ cost,
294
+ request_id,
295
+ summary,
296
+ exc.details(),
297
+ )
298
+ self.logger.debug(
299
+ "Polaris ReportClient failed address=%s code=%s cost=%.3fms "
300
+ "requestId=%s %s: %s",
301
+ address,
302
+ exc.code().name,
303
+ cost,
304
+ request_id,
305
+ summary,
306
+ exc.details(),
202
307
  )
203
- self.logger.debug("Polaris ReportClient failed address=%s code=%s cost=%.3fms: %s",
204
- address, exc.code().name, cost, exc.details())
205
308
  else:
206
309
  self.logger.error(
207
310
  "Polaris %s failed address=%s code=%s cost=%.3fms: %s",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polaris-python
3
- Version: 0.0.1.dev3
3
+ Version: 0.0.1.dev4
4
4
  Summary: Python SDK for Polaris service governance.
5
5
  Author-email: evelynwei <evelynwei@tencent.com>
6
6
  License-Expression: BSD-3-Clause
@@ -189,24 +189,36 @@ See [TLS](docs/guides/tls.md).
189
189
 
190
190
  ## Examples
191
191
 
192
- Run all offline verifications:
192
+ From the repository root, Quickstart reads its adjacent `polaris.yaml`:
193
193
 
194
194
  ```bash
195
- ./examples/verify_all.sh
195
+ export POLARIS_SERVER_ADDRESS=127.0.0.1:8091
196
+ export POLARIS_TOKEN=
197
+ make -C examples run DEMO=quickstart/provider ARGS='--service-token "" --port 0'
198
+ # Another terminal with the same environment
199
+ make -C examples run DEMO=quickstart/consumer ARGS='--port 18080'
196
200
  ```
197
201
 
198
- Run or inspect a single demo:
202
+ `--service-token` is required; an empty string works when service authentication is
203
+ disabled. Runs stay in the foreground; use separate terminals or `&`.
199
204
 
200
205
  ```bash
201
- cd examples/quickstart
202
- ./run.sh --help
203
- ./verify.sh
204
- ./logs.sh --latest --tail 30
205
- ./cleanup.sh --force
206
+ make -C examples logs ARGS='--only quickstart --latest --tail 30'
207
+ make -C examples cleanup ARGS='--only quickstart --dry-run'
208
+ make -C examples cleanup ARGS='--only quickstart'
206
209
  ```
207
210
 
208
- Start with [examples/README.md](examples/README.md). Real registry examples require
209
- `POLARIS_E2E=1`.
211
+ See [examples/README.md](examples/README.md) for demos, Makefiles and log/cleanup options.
212
+
213
+ Verification and test artifacts belong to `integration/`:
214
+
215
+ ```bash
216
+ ./integration/verify.sh --only quickstart
217
+ ./integration/verify_e2e.sh --only quickstart
218
+ ```
219
+
220
+ See the [integration guide](integration/README.md) and
221
+ [quickstart tests](integration/scenarios/quickstart/test.md) for checks and test artifacts.
210
222
 
211
223
  ## License
212
224
 
@@ -1,11 +1,11 @@
1
1
  polaris/__init__.py,sha256=g0tzWZkbmiA08jI9eamped66UghtWNHP52QNSgPeJ28,2471
2
- polaris/_version.py,sha256=KMXFk_FwAdUVDsP4hvXM2JXs47QArQgdsX84gjLynJ8,710
2
+ polaris/_version.py,sha256=24cjgsjZ51oQN2WKxw5dhXfg4gHwfuN2ubPhXnYC6W8,710
3
3
  polaris/plugin_manager.py,sha256=5hngFEOGMSGTeI0dnk-y4Y4Q4CXttS3fsZyxu-rGELg,5690
4
4
  polaris/api/__init__.py,sha256=tOUxtVmMBEdzZUP5YqfIIEWUiu1jtrOXpuSTFTHuOs0,1778
5
5
  polaris/api/_log_file.py,sha256=LYd__J8ooAdtONz7Ux7KmEridLpMyzKh0jk82BRsgpU,8729
6
6
  polaris/api/config.py,sha256=dFm7wKgbjpzZgLrF6yUQYPHW_kFJBmLXu-80mFF_RHw,6023
7
7
  polaris/api/consumer.py,sha256=etibb0SYWeqkVKGb_JDl4yCUxQ2r_AVU6YoB-Wj_S_s,10708
8
- polaris/api/log.py,sha256=3FvOC395PiIXbdxZfD5SlU9nMMpKktSn96OoDPk-T9s,17496
8
+ polaris/api/log.py,sha256=TDTv08enECNWalRBRErix6KPvKSBcZ4Zhp2UH6__P9M,17535
9
9
  polaris/api/lossless.py,sha256=Bg6srjmKU7mUNxlQkE2Ctpu_GyQgNr0Ijd7d-DPAxA0,3393
10
10
  polaris/api/provider.py,sha256=wZ8yize1GTuaeXR1WB15-y7OuU-esgKvgw2cXblKekU,6936
11
11
  polaris/api/skill.py,sha256=j3lCuU0H9Hhyc6OvA0tU9fE_g4lGmhEna3qUATXmXR0,8352
@@ -62,7 +62,7 @@ polaris/plugin/server_connector/__init__.py,sha256=vxOLRFc-K6p6WjDZdtNAe95DRDXIg
62
62
  polaris/plugin/server_connector/base.py,sha256=CbCGkZQP3ZuCw2BFlzOhTgxLACFpl9u4BzzUqb8tn9c,3129
63
63
  polaris/plugin/server_connector/convert.py,sha256=xhYAqkl2DpLuAyWYFrS8v6tFjKf0ZE26UXH9d3ghJTA,7506
64
64
  polaris/plugin/server_connector/grpc_connector/__init__.py,sha256=grRkHuo-nrdQxpBZyDEsNs8bLOKR0XGQfYTe7_NrQFw,883
65
- polaris/plugin/server_connector/grpc_connector/connection.py,sha256=EJ-w_YQN3gpfhJ0_6rQqkfsd4_kZKAc80heQmhi5Jxo,18373
65
+ polaris/plugin/server_connector/grpc_connector/connection.py,sha256=56sOGVKZZy3W1Cr59PJ4K97T2qne_y7ZI1u7PRKca1M,22488
66
66
  polaris/plugin/server_connector/grpc_connector/grpc_client.py,sha256=Q_qK2_5mDBXocqsa0MMpkpEZLtHAwUMD4lSnf3w8AG8,18425
67
67
  polaris/plugin/server_connector/grpc_connector/tls.py,sha256=iGLyeqAUOKZsltg4IJAxLnnQFDbCfXAwEWVGacK3NWg,1987
68
68
  polaris/plugin/service_router/__init__.py,sha256=MdDHSjiSE7gTKPRTNH5JCWI8o5t60oz4u1Zmaf1Re7E,993
@@ -77,8 +77,8 @@ polaris/plugin/skill/persistence.py,sha256=Lr6D6KbcxHBAkP-vTkMg2sfGMwLhqOxhLyVGL
77
77
  polaris/plugin/stat_reporter/__init__.py,sha256=dWFnNx7KLGCJoyIBjDDco8xvm_QgAr6RFnVd3IXj6oY,726
78
78
  polaris/plugin/stat_reporter/base.py,sha256=egAQK8ZBTXsFpnFK8fW9fgA_2xDq7BeeQah4aio9T0Y,1178
79
79
  polaris/plugin/stat_reporter/prometheus.py,sha256=so0H8CyaGsnJpPMsmW57ryqePyWIsLvQZJu-g2cl-c8,17311
80
- polaris_python-0.0.1.dev3.dist-info/licenses/LICENSE,sha256=YJW-rt35PlCxHBGzDNLAaMYMaUjgCrrH57V3MJTwpSI,1779
81
- polaris_python-0.0.1.dev3.dist-info/METADATA,sha256=6qohm4-Fxqc4fKYbBD6vShc_rcwWVSm9pVMRuWPDR0M,6938
82
- polaris_python-0.0.1.dev3.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
83
- polaris_python-0.0.1.dev3.dist-info/top_level.txt,sha256=w-dcIlTf_g1vjlHs3857yHM0OcmFnk4xJ2LgBYbPI6w,8
84
- polaris_python-0.0.1.dev3.dist-info/RECORD,,
80
+ polaris_python-0.0.1.dev4.dist-info/licenses/LICENSE,sha256=YJW-rt35PlCxHBGzDNLAaMYMaUjgCrrH57V3MJTwpSI,1779
81
+ polaris_python-0.0.1.dev4.dist-info/METADATA,sha256=GAP1ScgF4hBW6XAGvaICkEv14pO1OTpL8k7dBf0ztQc,7710
82
+ polaris_python-0.0.1.dev4.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
83
+ polaris_python-0.0.1.dev4.dist-info/top_level.txt,sha256=w-dcIlTf_g1vjlHs3857yHM0OcmFnk4xJ2LgBYbPI6w,8
84
+ polaris_python-0.0.1.dev4.dist-info/RECORD,,