langfun 0.1.2.dev202509280803__py3-none-any.whl → 0.1.2.dev202509300805__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.
Potentially problematic release.
This version of langfun might be problematic. Click here for more details.
- langfun/env/base_environment.py +19 -7
- langfun/env/base_sandbox.py +31 -8
- langfun/env/base_test.py +134 -102
- langfun/env/event_handlers/__init__.py +2 -0
- langfun/env/event_handlers/base.py +13 -0
- langfun/env/event_handlers/event_logger.py +45 -7
- langfun/env/event_handlers/event_logger_test.py +8 -0
- langfun/env/event_handlers/metric_writer.py +545 -0
- langfun/env/event_handlers/metric_writer_test.py +160 -0
- langfun/env/test_utils.py +6 -3
- {langfun-0.1.2.dev202509280803.dist-info → langfun-0.1.2.dev202509300805.dist-info}/METADATA +1 -1
- {langfun-0.1.2.dev202509280803.dist-info → langfun-0.1.2.dev202509300805.dist-info}/RECORD +15 -13
- {langfun-0.1.2.dev202509280803.dist-info → langfun-0.1.2.dev202509300805.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202509280803.dist-info → langfun-0.1.2.dev202509300805.dist-info}/licenses/LICENSE +0 -0
- {langfun-0.1.2.dev202509280803.dist-info → langfun-0.1.2.dev202509300805.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
# Copyright 2025 The Langfun Authors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
"""Environment event logger."""
|
|
15
|
+
|
|
16
|
+
from langfun.env.event_handlers import base
|
|
17
|
+
import pyglove as pg
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
_METRIC_NAMESPACE = '/langfun/env'
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MetricWriter(pg.Object, base.EventHandler):
|
|
24
|
+
"""Event handler for streamz metrics."""
|
|
25
|
+
|
|
26
|
+
def _get_counter(
|
|
27
|
+
self,
|
|
28
|
+
name: str,
|
|
29
|
+
description: str,
|
|
30
|
+
parameters: dict[str, type[str]] | None = None
|
|
31
|
+
) -> pg.monitoring.Counter:
|
|
32
|
+
return self._metric_collection.get_counter(
|
|
33
|
+
name=name,
|
|
34
|
+
description=description,
|
|
35
|
+
parameters=parameters
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def _get_scalar(
|
|
39
|
+
self,
|
|
40
|
+
name: str,
|
|
41
|
+
description: str,
|
|
42
|
+
parameters: dict[str, type[str]] | None = None
|
|
43
|
+
) -> pg.monitoring.Metric:
|
|
44
|
+
return self._metric_collection.get_scalar(
|
|
45
|
+
name=name,
|
|
46
|
+
description=description,
|
|
47
|
+
parameters=parameters
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def _initialize_metrics(self) -> None:
|
|
51
|
+
"""Initializes metrics."""
|
|
52
|
+
|
|
53
|
+
self._metric_collection = pg.monitoring.metric_collection(_METRIC_NAMESPACE)
|
|
54
|
+
|
|
55
|
+
#
|
|
56
|
+
# Environment metrics.
|
|
57
|
+
#
|
|
58
|
+
|
|
59
|
+
self._environment_housekeep_duration_ms = self._get_scalar(
|
|
60
|
+
'environment_housekeep_duration_ms',
|
|
61
|
+
description='Environment housekeeping duration in milliseconds',
|
|
62
|
+
parameters={
|
|
63
|
+
'environment_id': str,
|
|
64
|
+
'has_error': bool,
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
#
|
|
69
|
+
# Sandbox metrics.
|
|
70
|
+
#
|
|
71
|
+
|
|
72
|
+
# Sandbox counters.
|
|
73
|
+
self._sandbox_start = self._get_counter(
|
|
74
|
+
'sandbox_start',
|
|
75
|
+
description='Sandbox start counter',
|
|
76
|
+
parameters={
|
|
77
|
+
'environment_id': str,
|
|
78
|
+
'has_error': bool,
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
self._sandbox_shutdown = self._get_counter(
|
|
82
|
+
'sandbox_shutdown',
|
|
83
|
+
description='Sandbox shutdown counter',
|
|
84
|
+
parameters={
|
|
85
|
+
'environment_id': str,
|
|
86
|
+
'has_error': bool
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
self._sandbox_housekeep = self._get_counter(
|
|
90
|
+
'sandbox_housekeep',
|
|
91
|
+
description='Sandbox housekeeping counter',
|
|
92
|
+
parameters={
|
|
93
|
+
'environment_id': str,
|
|
94
|
+
'has_error': bool,
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
self._sandbox_activity = self._get_counter(
|
|
98
|
+
'sandbox_activity',
|
|
99
|
+
description='Sandbox activity counter',
|
|
100
|
+
parameters={
|
|
101
|
+
'environment_id': str,
|
|
102
|
+
'activity': str,
|
|
103
|
+
'has_error': bool,
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Sandbox scalars.
|
|
108
|
+
self._sandbox_lifetime_ms = self._get_scalar(
|
|
109
|
+
'sandbox_lifetime_ms',
|
|
110
|
+
description='Sandbox life time in milliseconds',
|
|
111
|
+
parameters={
|
|
112
|
+
'environment_id': str,
|
|
113
|
+
'has_error': bool,
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
self._sandbox_start_duration_ms = self._get_scalar(
|
|
117
|
+
'sandbox_start_duration_ms',
|
|
118
|
+
description='Sandbox start duration in milliseconds',
|
|
119
|
+
parameters={
|
|
120
|
+
'environment_id': str,
|
|
121
|
+
'has_error': bool,
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
self._sandbox_shutdown_duration_ms = self._get_scalar(
|
|
125
|
+
'sandbox_shutdown_duration_ms',
|
|
126
|
+
description='Sandbox shutdown duration in milliseconds',
|
|
127
|
+
parameters={
|
|
128
|
+
'environment_id': str,
|
|
129
|
+
'has_error': bool,
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
self._sandbox_housekeep_duration_ms = self._get_scalar(
|
|
133
|
+
'sandbox_housekeep_duration_ms',
|
|
134
|
+
description='Sandbox housekeeping duration in milliseconds',
|
|
135
|
+
parameters={
|
|
136
|
+
'environment_id': str,
|
|
137
|
+
'has_error': bool,
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
self._sandbox_status_duration_ms = self._get_scalar(
|
|
141
|
+
'sandbox_status_duration_ms',
|
|
142
|
+
description='Sandbox duration of specific status in milliseconds',
|
|
143
|
+
parameters={
|
|
144
|
+
'environment_id': str,
|
|
145
|
+
'status': str,
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
self._sandbox_activity_duration_ms = self._get_scalar(
|
|
149
|
+
'sandbox_activity_duration_ms',
|
|
150
|
+
description='Sandbox activity duration in milliseconds',
|
|
151
|
+
parameters={
|
|
152
|
+
'environment_id': str,
|
|
153
|
+
'activity': str,
|
|
154
|
+
'has_error': bool,
|
|
155
|
+
}
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
#
|
|
159
|
+
# Feature metrics.
|
|
160
|
+
#
|
|
161
|
+
|
|
162
|
+
# Feature counters.
|
|
163
|
+
self._feature_setup = self._get_counter(
|
|
164
|
+
'feature_setup',
|
|
165
|
+
description='Feature setup counter',
|
|
166
|
+
parameters={
|
|
167
|
+
'environment_id': str,
|
|
168
|
+
'feature_name': str,
|
|
169
|
+
'has_error': bool,
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
self._feature_teardown = self._get_counter(
|
|
173
|
+
'feature_teardown',
|
|
174
|
+
description='Feature teardown counter',
|
|
175
|
+
parameters={
|
|
176
|
+
'environment_id': str,
|
|
177
|
+
'feature_name': str,
|
|
178
|
+
'has_error': bool,
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
self._feature_setup_session = self._get_counter(
|
|
182
|
+
'feature_setup_session',
|
|
183
|
+
description='Feature setup session counter',
|
|
184
|
+
parameters={
|
|
185
|
+
'environment_id': str,
|
|
186
|
+
'feature_name': str,
|
|
187
|
+
'has_error': bool,
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
self._feature_teardown_session = self._get_counter(
|
|
191
|
+
'feature_teardown_session',
|
|
192
|
+
description='Feature teardown session counter',
|
|
193
|
+
parameters={
|
|
194
|
+
'environment_id': str,
|
|
195
|
+
'feature_name': str,
|
|
196
|
+
'has_error': bool,
|
|
197
|
+
}
|
|
198
|
+
)
|
|
199
|
+
self._feature_housekeep = self._get_counter(
|
|
200
|
+
'feature_housekeep',
|
|
201
|
+
description='Feature housekeeping counter',
|
|
202
|
+
parameters={
|
|
203
|
+
'environment_id': str,
|
|
204
|
+
'feature_name': str,
|
|
205
|
+
'has_error': bool,
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Feature scalars.
|
|
210
|
+
self._feature_setup_duration_ms = self._get_scalar(
|
|
211
|
+
'feature_setup_duration_ms',
|
|
212
|
+
description='Feature setup duration in milliseconds',
|
|
213
|
+
parameters={
|
|
214
|
+
'environment_id': str,
|
|
215
|
+
'feature_name': str,
|
|
216
|
+
'has_error': bool,
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
self._feature_teardown_duration_ms = self._get_scalar(
|
|
220
|
+
'feature_teardown_duration_ms',
|
|
221
|
+
description='Feature teardown duration in milliseconds',
|
|
222
|
+
parameters={
|
|
223
|
+
'environment_id': str,
|
|
224
|
+
'feature_name': str,
|
|
225
|
+
'has_error': bool,
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
self._feature_setup_session_duration_ms = self._get_scalar(
|
|
229
|
+
'feature_setup_session_duration_ms',
|
|
230
|
+
description='Feature setup session duration in milliseconds',
|
|
231
|
+
parameters={
|
|
232
|
+
'environment_id': str,
|
|
233
|
+
'feature_name': str,
|
|
234
|
+
'has_error': bool,
|
|
235
|
+
}
|
|
236
|
+
)
|
|
237
|
+
self._feature_teardown_session_duration_ms = self._get_scalar(
|
|
238
|
+
'feature_teardown_session_duration_ms',
|
|
239
|
+
description='Feature teardown session duration in milliseconds',
|
|
240
|
+
parameters={
|
|
241
|
+
'environment_id': str,
|
|
242
|
+
'feature_name': str,
|
|
243
|
+
'has_error': bool,
|
|
244
|
+
}
|
|
245
|
+
)
|
|
246
|
+
self._feature_housekeep_duration_ms = self._get_scalar(
|
|
247
|
+
'feature_housekeep_duration_ms',
|
|
248
|
+
description='Feature housekeeping duration in milliseconds',
|
|
249
|
+
parameters={
|
|
250
|
+
'environment_id': str,
|
|
251
|
+
'feature_name': str,
|
|
252
|
+
'has_error': bool,
|
|
253
|
+
}
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
#
|
|
257
|
+
# Session metrics.
|
|
258
|
+
#
|
|
259
|
+
|
|
260
|
+
self._session_start_duration_ms = self._get_scalar(
|
|
261
|
+
'session_start_duration_ms',
|
|
262
|
+
description='Session start duration in milliseconds',
|
|
263
|
+
parameters={
|
|
264
|
+
'environment_id': str,
|
|
265
|
+
'has_error': bool,
|
|
266
|
+
}
|
|
267
|
+
)
|
|
268
|
+
self._session_end_duration_ms = self._get_scalar(
|
|
269
|
+
'session_end_duration_ms',
|
|
270
|
+
description='Session end duration in milliseconds',
|
|
271
|
+
parameters={
|
|
272
|
+
'environment_id': str,
|
|
273
|
+
'has_error': bool,
|
|
274
|
+
}
|
|
275
|
+
)
|
|
276
|
+
self._session_lifetime_ms = self._get_scalar(
|
|
277
|
+
'session_lifetime_ms',
|
|
278
|
+
description='Session lifetime in milliseconds',
|
|
279
|
+
parameters={
|
|
280
|
+
'environment_id': str,
|
|
281
|
+
'has_error': bool,
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
def on_environment_starting(
|
|
286
|
+
self,
|
|
287
|
+
environment: base.Environment
|
|
288
|
+
) -> None:
|
|
289
|
+
"""Called when the environment is starting."""
|
|
290
|
+
self._initialize_metrics()
|
|
291
|
+
|
|
292
|
+
def on_environment_housekeep(
|
|
293
|
+
self,
|
|
294
|
+
environment: base.Environment,
|
|
295
|
+
counter: int,
|
|
296
|
+
duration: float,
|
|
297
|
+
error: BaseException | None
|
|
298
|
+
) -> None:
|
|
299
|
+
"""Called when the environment is housekeeping."""
|
|
300
|
+
self._environment_housekeep_duration_ms.record(
|
|
301
|
+
int(duration * 1000),
|
|
302
|
+
environment_id=str(environment.id),
|
|
303
|
+
has_error=error is not None
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
def on_sandbox_start(
|
|
307
|
+
self,
|
|
308
|
+
environment: base.Environment,
|
|
309
|
+
sandbox: base.Sandbox,
|
|
310
|
+
duration: float,
|
|
311
|
+
error: BaseException | None
|
|
312
|
+
) -> None:
|
|
313
|
+
self._sandbox_start.increment(
|
|
314
|
+
environment_id=str(environment.id),
|
|
315
|
+
has_error=error is not None
|
|
316
|
+
)
|
|
317
|
+
self._sandbox_start_duration_ms.record(
|
|
318
|
+
int(duration * 1000),
|
|
319
|
+
environment_id=str(environment.id),
|
|
320
|
+
has_error=error is not None
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
def on_sandbox_status_change(
|
|
324
|
+
self,
|
|
325
|
+
environment: base.Environment,
|
|
326
|
+
sandbox: base.Sandbox,
|
|
327
|
+
old_status: base.Sandbox.Status,
|
|
328
|
+
new_status: base.Sandbox.Status,
|
|
329
|
+
span: float
|
|
330
|
+
) -> None:
|
|
331
|
+
self._sandbox_status_duration_ms.record(
|
|
332
|
+
int(span * 1000),
|
|
333
|
+
environment_id=str(environment.id),
|
|
334
|
+
status=old_status.value
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
def on_sandbox_shutdown(
|
|
338
|
+
self,
|
|
339
|
+
environment: base.Environment,
|
|
340
|
+
sandbox: base.Sandbox,
|
|
341
|
+
duration: float,
|
|
342
|
+
lifetime: float,
|
|
343
|
+
error: BaseException | None
|
|
344
|
+
) -> None:
|
|
345
|
+
self._sandbox_shutdown.increment(
|
|
346
|
+
environment_id=str(environment.id),
|
|
347
|
+
has_error=error is not None
|
|
348
|
+
)
|
|
349
|
+
self._sandbox_shutdown_duration_ms.record(
|
|
350
|
+
int(duration * 1000),
|
|
351
|
+
environment_id=str(environment.id),
|
|
352
|
+
has_error=error is not None
|
|
353
|
+
)
|
|
354
|
+
self._sandbox_lifetime_ms.record(
|
|
355
|
+
int(lifetime * 1000),
|
|
356
|
+
environment_id=str(environment.id),
|
|
357
|
+
has_error=error is not None
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
def on_sandbox_housekeep(
|
|
361
|
+
self,
|
|
362
|
+
environment: base.Environment,
|
|
363
|
+
sandbox: base.Sandbox,
|
|
364
|
+
counter: int,
|
|
365
|
+
duration: float,
|
|
366
|
+
error: BaseException | None
|
|
367
|
+
) -> None:
|
|
368
|
+
"""Called when a sandbox feature is housekeeping."""
|
|
369
|
+
self._sandbox_housekeep.increment(
|
|
370
|
+
environment_id=str(environment.id),
|
|
371
|
+
has_error=error is not None
|
|
372
|
+
)
|
|
373
|
+
self._sandbox_housekeep_duration_ms.record(
|
|
374
|
+
int(duration * 1000),
|
|
375
|
+
environment_id=str(environment.id),
|
|
376
|
+
has_error=error is not None
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
def on_feature_setup(
|
|
380
|
+
self,
|
|
381
|
+
environment: base.Environment,
|
|
382
|
+
sandbox: base.Sandbox,
|
|
383
|
+
feature: base.Feature,
|
|
384
|
+
duration: float,
|
|
385
|
+
error: BaseException | None
|
|
386
|
+
) -> None:
|
|
387
|
+
"""Called when a sandbox feature is setup."""
|
|
388
|
+
self._feature_setup.increment(
|
|
389
|
+
environment_id=str(environment.id),
|
|
390
|
+
feature_name=feature.name,
|
|
391
|
+
has_error=error is not None
|
|
392
|
+
)
|
|
393
|
+
self._feature_setup_duration_ms.record(
|
|
394
|
+
int(duration * 1000),
|
|
395
|
+
environment_id=str(environment.id),
|
|
396
|
+
feature_name=feature.name,
|
|
397
|
+
has_error=error is not None
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
def on_feature_teardown(
|
|
401
|
+
self,
|
|
402
|
+
environment: base.Environment,
|
|
403
|
+
sandbox: base.Sandbox,
|
|
404
|
+
feature: base.Feature,
|
|
405
|
+
duration: float,
|
|
406
|
+
error: BaseException | None
|
|
407
|
+
) -> None:
|
|
408
|
+
"""Called when a sandbox feature is teardown."""
|
|
409
|
+
self._feature_teardown.increment(
|
|
410
|
+
environment_id=str(environment.id),
|
|
411
|
+
feature_name=feature.name,
|
|
412
|
+
has_error=error is not None
|
|
413
|
+
)
|
|
414
|
+
self._feature_teardown_duration_ms.record(
|
|
415
|
+
int(duration * 1000),
|
|
416
|
+
environment_id=str(environment.id),
|
|
417
|
+
feature_name=feature.name,
|
|
418
|
+
has_error=error is not None
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
def on_feature_setup_session(
|
|
422
|
+
self,
|
|
423
|
+
environment: base.Environment,
|
|
424
|
+
sandbox: base.Sandbox,
|
|
425
|
+
feature: base.Feature,
|
|
426
|
+
session_id: str | None,
|
|
427
|
+
duration: float,
|
|
428
|
+
error: BaseException | None
|
|
429
|
+
) -> None:
|
|
430
|
+
"""Called when a sandbox feature is setup."""
|
|
431
|
+
self._feature_setup_session.increment(
|
|
432
|
+
environment_id=str(environment.id),
|
|
433
|
+
feature_name=feature.name,
|
|
434
|
+
has_error=error is not None
|
|
435
|
+
)
|
|
436
|
+
self._feature_setup_session_duration_ms.record(
|
|
437
|
+
int(duration * 1000),
|
|
438
|
+
environment_id=str(environment.id),
|
|
439
|
+
feature_name=feature.name,
|
|
440
|
+
has_error=error is not None
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
def on_feature_teardown_session(
|
|
444
|
+
self,
|
|
445
|
+
environment: base.Environment,
|
|
446
|
+
sandbox: base.Sandbox,
|
|
447
|
+
feature: base.Feature,
|
|
448
|
+
session_id: str,
|
|
449
|
+
duration: float,
|
|
450
|
+
error: BaseException | None
|
|
451
|
+
) -> None:
|
|
452
|
+
"""Called when a sandbox feature is teardown."""
|
|
453
|
+
self._feature_teardown_session.increment(
|
|
454
|
+
environment_id=str(environment.id),
|
|
455
|
+
feature_name=feature.name,
|
|
456
|
+
has_error=error is not None
|
|
457
|
+
)
|
|
458
|
+
self._feature_teardown_session_duration_ms.record(
|
|
459
|
+
int(duration * 1000),
|
|
460
|
+
environment_id=str(environment.id),
|
|
461
|
+
feature_name=feature.name,
|
|
462
|
+
has_error=error is not None
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
def on_feature_housekeep(
|
|
466
|
+
self,
|
|
467
|
+
environment: base.Environment,
|
|
468
|
+
sandbox: base.Sandbox,
|
|
469
|
+
feature: base.Feature,
|
|
470
|
+
counter: int,
|
|
471
|
+
duration: float,
|
|
472
|
+
error: BaseException | None
|
|
473
|
+
) -> None:
|
|
474
|
+
"""Called when a sandbox feature is housekeeping."""
|
|
475
|
+
self._feature_housekeep.increment(
|
|
476
|
+
environment_id=str(environment.id),
|
|
477
|
+
feature_name=feature.name,
|
|
478
|
+
has_error=error is not None
|
|
479
|
+
)
|
|
480
|
+
self._feature_housekeep_duration_ms.record(
|
|
481
|
+
int(duration * 1000),
|
|
482
|
+
environment_id=str(environment.id),
|
|
483
|
+
feature_name=feature.name,
|
|
484
|
+
has_error=error is not None
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
def on_session_start(
|
|
488
|
+
self,
|
|
489
|
+
environment: base.Environment,
|
|
490
|
+
sandbox: base.Sandbox,
|
|
491
|
+
session_id: str,
|
|
492
|
+
duration: float,
|
|
493
|
+
error: BaseException | None
|
|
494
|
+
) -> None:
|
|
495
|
+
"""Called when a sandbox session starts."""
|
|
496
|
+
self._session_start_duration_ms.record(
|
|
497
|
+
int(duration * 1000),
|
|
498
|
+
environment_id=str(environment.id),
|
|
499
|
+
has_error=error is not None
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
def on_session_end(
|
|
503
|
+
self,
|
|
504
|
+
environment: base.Environment,
|
|
505
|
+
sandbox: base.Sandbox,
|
|
506
|
+
session_id: str,
|
|
507
|
+
duration: float,
|
|
508
|
+
lifetime: float,
|
|
509
|
+
error: BaseException | None
|
|
510
|
+
) -> None:
|
|
511
|
+
"""Called when a sandbox session ends."""
|
|
512
|
+
self._session_end_duration_ms.record(
|
|
513
|
+
int(duration * 1000),
|
|
514
|
+
environment_id=str(environment.id),
|
|
515
|
+
has_error=error is not None
|
|
516
|
+
)
|
|
517
|
+
self._session_lifetime_ms.record(
|
|
518
|
+
int(lifetime * 1000),
|
|
519
|
+
environment_id=str(environment.id),
|
|
520
|
+
has_error=error is not None
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
def on_sandbox_activity(
|
|
524
|
+
self,
|
|
525
|
+
name: str,
|
|
526
|
+
environment: base.Environment,
|
|
527
|
+
sandbox: base.Sandbox,
|
|
528
|
+
feature: base.Feature | None,
|
|
529
|
+
session_id: str | None,
|
|
530
|
+
duration: float,
|
|
531
|
+
error: BaseException | None,
|
|
532
|
+
**kwargs
|
|
533
|
+
) -> None:
|
|
534
|
+
"""Called when a sandbox activity is performed."""
|
|
535
|
+
self._sandbox_activity.increment(
|
|
536
|
+
environment_id=str(environment.id),
|
|
537
|
+
activity=name,
|
|
538
|
+
has_error=error is not None
|
|
539
|
+
)
|
|
540
|
+
self._sandbox_activity_duration_ms.record(
|
|
541
|
+
int(duration * 1000),
|
|
542
|
+
environment_id=str(environment.id),
|
|
543
|
+
activity=name,
|
|
544
|
+
has_error=error is not None
|
|
545
|
+
)
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Copyright 2025 The Langfun Authors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import unittest
|
|
16
|
+
|
|
17
|
+
from langfun.env import interface
|
|
18
|
+
from langfun.env import test_utils
|
|
19
|
+
from langfun.env.event_handlers import metric_writer as metric_writer_lib
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MetricWriterTest(unittest.TestCase):
|
|
23
|
+
|
|
24
|
+
def test_write_metric(self):
|
|
25
|
+
writer = metric_writer_lib.MetricWriter()
|
|
26
|
+
env = test_utils.TestingEnvironment(
|
|
27
|
+
features={
|
|
28
|
+
'test_feature1': test_utils.TestingFeature(housekeep_interval=0),
|
|
29
|
+
'test_feature2': test_utils.TestingFeature(housekeep_interval=None),
|
|
30
|
+
},
|
|
31
|
+
pool_size=2,
|
|
32
|
+
outage_grace_period=0,
|
|
33
|
+
outage_retry_interval=0,
|
|
34
|
+
housekeep_interval=0.0,
|
|
35
|
+
sandbox_keepalive_interval=1.0,
|
|
36
|
+
event_handlers=[writer],
|
|
37
|
+
)
|
|
38
|
+
with env:
|
|
39
|
+
with env.sandbox('session1') as sb:
|
|
40
|
+
self.assertEqual(sb.test_feature1.num_shell_calls(), 4)
|
|
41
|
+
|
|
42
|
+
with self.assertRaises(interface.SandboxStateError):
|
|
43
|
+
with env.sandbox('session2') as sb:
|
|
44
|
+
sb.shell('echo "bar"', raise_error=RuntimeError)
|
|
45
|
+
|
|
46
|
+
self.assertEqual(
|
|
47
|
+
writer._sandbox_start.value(
|
|
48
|
+
environment_id='testing-env', has_error=False
|
|
49
|
+
),
|
|
50
|
+
2
|
|
51
|
+
)
|
|
52
|
+
self.assertGreater(
|
|
53
|
+
writer._sandbox_housekeep.value(
|
|
54
|
+
environment_id='testing-env',
|
|
55
|
+
has_error=False
|
|
56
|
+
),
|
|
57
|
+
0,
|
|
58
|
+
)
|
|
59
|
+
self.assertEqual(
|
|
60
|
+
writer._sandbox_shutdown.value(
|
|
61
|
+
environment_id='testing-env', has_error=False
|
|
62
|
+
),
|
|
63
|
+
2
|
|
64
|
+
)
|
|
65
|
+
self.assertEqual(
|
|
66
|
+
writer._feature_setup.value(
|
|
67
|
+
environment_id='testing-env',
|
|
68
|
+
feature_name='test_feature1',
|
|
69
|
+
has_error=False
|
|
70
|
+
),
|
|
71
|
+
2,
|
|
72
|
+
)
|
|
73
|
+
self.assertEqual(
|
|
74
|
+
writer._feature_setup.value(
|
|
75
|
+
environment_id='testing-env',
|
|
76
|
+
feature_name='test_feature2',
|
|
77
|
+
has_error=False
|
|
78
|
+
),
|
|
79
|
+
2,
|
|
80
|
+
)
|
|
81
|
+
self.assertEqual(
|
|
82
|
+
writer._feature_setup_session.value(
|
|
83
|
+
environment_id='testing-env',
|
|
84
|
+
feature_name='test_feature1',
|
|
85
|
+
has_error=False
|
|
86
|
+
),
|
|
87
|
+
3,
|
|
88
|
+
)
|
|
89
|
+
self.assertEqual(
|
|
90
|
+
writer._feature_setup_session.value(
|
|
91
|
+
environment_id='testing-env',
|
|
92
|
+
feature_name='test_feature2',
|
|
93
|
+
has_error=False
|
|
94
|
+
),
|
|
95
|
+
3,
|
|
96
|
+
)
|
|
97
|
+
self.assertEqual(
|
|
98
|
+
writer._feature_teardown_session.value(
|
|
99
|
+
environment_id='testing-env',
|
|
100
|
+
feature_name='test_feature1',
|
|
101
|
+
has_error=False
|
|
102
|
+
),
|
|
103
|
+
2,
|
|
104
|
+
)
|
|
105
|
+
self.assertEqual(
|
|
106
|
+
writer._feature_teardown_session.value(
|
|
107
|
+
environment_id='testing-env',
|
|
108
|
+
feature_name='test_feature2',
|
|
109
|
+
has_error=False
|
|
110
|
+
),
|
|
111
|
+
2,
|
|
112
|
+
)
|
|
113
|
+
self.assertEqual(
|
|
114
|
+
writer._feature_teardown.value(
|
|
115
|
+
environment_id='testing-env',
|
|
116
|
+
feature_name='test_feature1',
|
|
117
|
+
has_error=False
|
|
118
|
+
),
|
|
119
|
+
2,
|
|
120
|
+
)
|
|
121
|
+
self.assertEqual(
|
|
122
|
+
writer._feature_teardown.value(
|
|
123
|
+
environment_id='testing-env',
|
|
124
|
+
feature_name='test_feature2',
|
|
125
|
+
has_error=False
|
|
126
|
+
),
|
|
127
|
+
2,
|
|
128
|
+
)
|
|
129
|
+
self.assertGreater(
|
|
130
|
+
writer._feature_housekeep.value(
|
|
131
|
+
environment_id='testing-env',
|
|
132
|
+
feature_name='test_feature1',
|
|
133
|
+
has_error=False
|
|
134
|
+
),
|
|
135
|
+
0,
|
|
136
|
+
)
|
|
137
|
+
self.assertEqual(
|
|
138
|
+
writer._feature_housekeep.value(
|
|
139
|
+
environment_id='testing-env',
|
|
140
|
+
feature_name='test_feature2',
|
|
141
|
+
has_error=False
|
|
142
|
+
),
|
|
143
|
+
0,
|
|
144
|
+
)
|
|
145
|
+
self.assertEqual(
|
|
146
|
+
writer._sandbox_activity.value(
|
|
147
|
+
environment_id='testing-env', activity='shell', has_error=False
|
|
148
|
+
),
|
|
149
|
+
18
|
|
150
|
+
)
|
|
151
|
+
self.assertEqual(
|
|
152
|
+
writer._sandbox_activity.value(
|
|
153
|
+
environment_id='testing-env', activity='shell', has_error=True
|
|
154
|
+
),
|
|
155
|
+
1
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
if __name__ == '__main__':
|
|
160
|
+
unittest.main()
|