glean-parser 19.2.0__tar.gz → 20.0.1__tar.gz

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.
Files changed (153) hide show
  1. {glean_parser-19.2.0 → glean_parser-20.0.1}/PKG-INFO +1 -1
  2. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/go_server.py +31 -21
  3. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/metrics.py +4 -0
  4. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/schemas/metrics.2-0-0.schema.yaml +25 -0
  5. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/go_server.jinja2 +79 -94
  6. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/rust.jinja2 +1 -0
  7. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/translate.py +0 -1
  8. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/translation_options.py +5 -0
  9. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/util.py +1 -0
  10. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/all_metrics.yaml +1 -0
  11. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/server_custom_ping_only_compare.go +55 -35
  12. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/server_events_and_custom_ping_compare.go +86 -48
  13. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/server_events_only_compare.go +55 -35
  14. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_go_server_pubsub.py +46 -9
  15. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_javascript.py +1 -1
  16. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_kotlin.py +5 -1
  17. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_metrics.py +58 -0
  18. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_parser.py +2 -0
  19. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_rust.py +18 -0
  20. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_swift.py +4 -0
  21. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_validate_ping.py +14 -7
  22. {glean_parser-19.2.0 → glean_parser-20.0.1}/.gitignore +0 -0
  23. {glean_parser-19.2.0 → glean_parser-20.0.1}/AUTHORS.md +0 -0
  24. {glean_parser-19.2.0 → glean_parser-20.0.1}/LICENSE +0 -0
  25. {glean_parser-19.2.0 → glean_parser-20.0.1}/README.md +0 -0
  26. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/__init__.py +0 -0
  27. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/__main__.py +0 -0
  28. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/data_review.py +0 -0
  29. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/javascript.py +0 -0
  30. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/javascript_server.py +0 -0
  31. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/kotlin.py +0 -0
  32. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/lint.py +0 -0
  33. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/markdown.py +0 -0
  34. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/parser.py +0 -0
  35. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/pings.py +0 -0
  36. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/python_server.py +0 -0
  37. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/ruby_server.py +0 -0
  38. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/rust.py +0 -0
  39. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/rust_server.py +0 -0
  40. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/rust_sym.py +0 -0
  41. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/schemas/metrics.1-0-0.schema.yaml +0 -0
  42. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/schemas/pings.1-0-0.schema.yaml +0 -0
  43. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/schemas/pings.2-0-0.schema.yaml +0 -0
  44. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/schemas/tags.1-0-0.schema.yaml +0 -0
  45. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/swift.py +0 -0
  46. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/tags.py +0 -0
  47. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/data_review.jinja2 +0 -0
  48. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/javascript.buildinfo.jinja2 +0 -0
  49. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/javascript.jinja2 +0 -0
  50. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/javascript_server.jinja2 +0 -0
  51. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/kotlin.buildinfo.jinja2 +0 -0
  52. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/kotlin.jinja2 +0 -0
  53. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/markdown.jinja2 +0 -0
  54. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/python_server.jinja2 +0 -0
  55. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/qmldir.jinja2 +0 -0
  56. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/ruby_server.jinja2 +0 -0
  57. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/rust_server.jinja2 +0 -0
  58. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/rust_sym.jinja2 +0 -0
  59. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/templates/swift.jinja2 +0 -0
  60. {glean_parser-19.2.0 → glean_parser-20.0.1}/glean_parser/validate_ping.py +0 -0
  61. {glean_parser-19.2.0 → glean_parser-20.0.1}/pyproject.toml +0 -0
  62. {glean_parser-19.2.0 → glean_parser-20.0.1}/server_telemetry/sdk-metrics-compat.yaml +0 -0
  63. {glean_parser-19.2.0 → glean_parser-20.0.1}/server_telemetry/server-side-pings.yaml +0 -0
  64. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/conftest.py +0 -0
  65. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/all_pings.yaml +0 -0
  66. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/attribution.yaml +0 -0
  67. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/bad_attribution.yamlx +0 -0
  68. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/bad_ping.yamlx +0 -0
  69. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/core.yaml +0 -0
  70. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/custom_ping_no_event_metrics.yaml +0 -0
  71. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/custom_ping_no_event_pings.yaml +0 -0
  72. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/dual_labeled.yaml +0 -0
  73. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/dual_labeled_invalid.yaml +0 -0
  74. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/duplicate_labeled.yaml +0 -0
  75. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/duplicate_send_in_ping.yaml +0 -0
  76. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/empty.yaml +0 -0
  77. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/event_key_ordering.yaml +0 -0
  78. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/events_data_sensitivity.yaml +0 -0
  79. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/events_with_types.yaml +0 -0
  80. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/fxa-server-metrics.yaml +0 -0
  81. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/fxa-server-pings.yaml +0 -0
  82. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/go_server_custom_ping_only_metrics.yaml +0 -0
  83. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/go_server_custom_ping_only_pings.yaml +0 -0
  84. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/go_server_events_and_custom_ping_metrics.yaml +0 -0
  85. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/go_server_events_and_custom_ping_pings.yaml +0 -0
  86. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/go_server_events_only_metrics.yaml +0 -0
  87. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/go_server_labeled_boolean_metrics.yaml +0 -0
  88. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/go_server_metrics_unsupported.yaml +0 -0
  89. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/invalid-ping-names.yaml +0 -0
  90. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/invalid.yamlx +0 -0
  91. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/jwe.yaml +0 -0
  92. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/metric-with-tags.yaml +0 -0
  93. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/mixed-expirations.yaml +0 -0
  94. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/name_too_similar.yaml +0 -0
  95. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/object.yaml +0 -0
  96. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/old_event_api.yamlx +0 -0
  97. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/ordering.yaml +0 -0
  98. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/pings.yaml +0 -0
  99. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/python_server_metrics_unsupported.yaml +0 -0
  100. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/rate.yaml +0 -0
  101. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/redefined_category.yamlx +0 -0
  102. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/redefined_metric.yamlx +0 -0
  103. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/redefined_ping.yamlx +0 -0
  104. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/reserved_categories.yamlx +0 -0
  105. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/ruby_server_metrics_unsupported.yaml +0 -0
  106. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/ruby_server_pings_unsupported.yaml +0 -0
  107. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/rust_server_custom_ping_only_metrics.yaml +0 -0
  108. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/rust_server_custom_ping_only_pings.yaml +0 -0
  109. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/rust_server_events_and_custom_ping_metrics.yaml +0 -0
  110. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/rust_server_events_and_custom_ping_pings.yaml +0 -0
  111. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/rust_server_events_only_metrics.yaml +0 -0
  112. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/rust_server_metrics_unsupported.yaml +0 -0
  113. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/same_name_different_category.yaml +0 -0
  114. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/schema-violation.yaml +0 -0
  115. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/send_if_empty_with_metrics.yaml +0 -0
  116. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/server_custom_ping_only_compare.rs +0 -0
  117. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/server_events_and_custom_ping_compare.rs +0 -0
  118. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/server_events_compare.rb +0 -0
  119. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/server_events_only_compare.rs +0 -0
  120. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/server_metrics_no_events_no_pings.yaml +0 -0
  121. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/server_metrics_with_event.yaml +0 -0
  122. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/server_pings.yaml +0 -0
  123. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/single_labeled.yaml +0 -0
  124. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/smaller.yaml +0 -0
  125. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/tags.yaml +0 -0
  126. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/telemetry_mirror.yaml +0 -0
  127. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/text.yaml +0 -0
  128. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/text_invalid.yaml +0 -0
  129. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/unknown_ping_used.yaml +0 -0
  130. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/wrong_key.yamlx +0 -0
  131. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/data/yaml_nits.yamlx +0 -0
  132. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/detekt.yml +0 -0
  133. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test-go/test.go.tmpl +0 -0
  134. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test-go/test_publisher.go.tmpl +0 -0
  135. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test-js/package.json +0 -0
  136. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test-js/test.js.tmpl +0 -0
  137. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test-py/test.py +0 -0
  138. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test-rb/test.rb.tmpl +0 -0
  139. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test-rs/test.rs.tmpl +0 -0
  140. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_cli.py +0 -0
  141. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_go_server.py +0 -0
  142. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_javascript_server.py +0 -0
  143. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_lint.py +0 -0
  144. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_markdown.py +0 -0
  145. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_pings.py +0 -0
  146. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_python_server.py +0 -0
  147. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_ruby_server.py +0 -0
  148. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_rust_server.py +0 -0
  149. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_rust_sym.py +0 -0
  150. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_tags.py +0 -0
  151. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_translate.py +0 -0
  152. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/test_utils.py +0 -0
  153. {glean_parser-19.2.0 → glean_parser-20.0.1}/tests/util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glean-parser
3
- Version: 19.2.0
3
+ Version: 20.0.1
4
4
  Summary: Parser tools for Mozilla's Glean telemetry
5
5
  Project-URL: Homepage, https://mozilla.github.io/glean
6
6
  Project-URL: Repository, https://github.com/mozilla/glean_parser
@@ -13,9 +13,11 @@ in server-side environments. In these environments SDK assumptions to measuremen
13
13
  window and connectivity don't hold.
14
14
 
15
15
  Generated code takes care of assembling pings with metrics, and serializing to messages
16
- conforming to Glean schema. Two transport modes are supported:
17
- - Cloud Logging (go_server): Logs to stdout in MozLog format for ingestion via GCP log routing
18
- - Pub/Sub (go_server_pubsub): Publishes directly to GCP Pub/Sub topics
16
+ conforming to Glean schema. The transport is selected with the `transport` option
17
+ on the `go_server` outputter (`-s transport=...`):
18
+ - `logging` (default): Logs to stdout in MozLog format for ingestion via GCP log routing
19
+ - `pubsub`: Builds messages for direct publishing to GCP Pub/Sub topics
20
+ - `combined`: Emits both in a single file (sharing common types) for gradual migration
19
21
 
20
22
  Warning: this outputter supports limited set of metrics,
21
23
  see `SUPPORTED_METRIC_TYPES` below.
@@ -128,8 +130,9 @@ def output_go(
128
130
  :param objects: A tree of objects (metrics and pings) as returned from
129
131
  `parser.parse_objects`.
130
132
  :param output_dir: Path to an output directory to write to.
131
- :param transport: Transport mode - either "logging" (Cloud Logging) or
132
- "pubsub" (Pub/Sub direct publishing). Default is "logging".
133
+ :param transport: Transport mode - one of "logging" (Cloud Logging, the
134
+ default), "pubsub" (Pub/Sub direct publishing), or "combined" (both in
135
+ a single file, sharing common types).
133
136
  """
134
137
 
135
138
  template = util.get_jinja2_template(
@@ -211,29 +214,36 @@ def output_go(
211
214
  )
212
215
 
213
216
 
214
- def output_go_logger(
215
- objs: metrics.ObjectTree, output_dir: Path, options: Optional[Dict[str, Any]] = None
216
- ) -> None:
217
- """
218
- Given a tree of objects, output Go code using Cloud Logging transport.
219
-
220
- :param objects: A tree of objects (metrics and pings) as returned from
221
- `parser.parse_objects`.
222
- :param output_dir: Path to an output directory to write to.
223
- :param options: options dictionary (currently unused for Go).
224
- """
225
- output_go(objs, output_dir, options, transport="logging")
217
+ def _resolve_transport(default: str, options: Optional[Dict[str, Any]]) -> str:
218
+ if not options:
219
+ return default
220
+ transport = options.get("transport")
221
+ if transport is None:
222
+ return default
223
+ if transport not in ("logging", "pubsub", "combined"):
224
+ raise ValueError(
225
+ f"Invalid transport '{transport}'."
226
+ " Must be one of: logging, pubsub, combined."
227
+ )
228
+ return transport
226
229
 
227
230
 
228
- def output_go_pubsub(
231
+ def output_go_logger(
229
232
  objs: metrics.ObjectTree, output_dir: Path, options: Optional[Dict[str, Any]] = None
230
233
  ) -> None:
231
234
  """
232
- Given a tree of objects, output Go code using Pub/Sub transport.
235
+ Given a tree of objects, output server Go code.
236
+
237
+ Defaults to the Cloud Logging transport; pass `-s transport=pubsub` for
238
+ direct Pub/Sub publishing, or `-s transport=combined` to emit both in a
239
+ single file (sharing common types) for a gradual logging->pubsub migration.
233
240
 
234
241
  :param objects: A tree of objects (metrics and pings) as returned from
235
242
  `parser.parse_objects`.
236
243
  :param output_dir: Path to an output directory to write to.
237
- :param options: options dictionary (currently unused for Go).
244
+ :param options: options dictionary. Supports `transport` key with values
245
+ `logging` (default), `pubsub`, or `combined`.
238
246
  """
239
- output_go(objs, output_dir, options, transport="pubsub")
247
+ output_go(
248
+ objs, output_dir, options, transport=_resolve_transport("logging", options)
249
+ )
@@ -61,6 +61,7 @@ class Metric:
61
61
  data_sensitivity: Optional[List[str]] = None,
62
62
  defined_in: Optional[Dict] = None,
63
63
  telemetry_mirror: Optional[str] = None,
64
+ in_session: bool = False,
64
65
  _config: Optional[Dict[str, Any]] = None,
65
66
  _validated: bool = False,
66
67
  ):
@@ -98,6 +99,8 @@ class Metric:
98
99
  self.defined_in = defined_in
99
100
  if telemetry_mirror is not None:
100
101
  self.telemetry_mirror = telemetry_mirror
102
+ if not hasattr(self, "in_session"):
103
+ self.in_session = in_session
101
104
 
102
105
  # _validated indicates whether this metric has already been jsonschema
103
106
  # validated (but not any of the Python-level validation).
@@ -312,6 +315,7 @@ class Event(Metric):
312
315
 
313
316
  def __init__(self, *args, **kwargs):
314
317
  self.extra_keys = kwargs.pop("extra_keys", {})
318
+ self.in_session = kwargs.pop("in_session", True)
315
319
  self.validate_extra_keys(self.extra_keys, kwargs.get("_config", {}))
316
320
  super().__init__(*args, **kwargs)
317
321
  self._generate_enums = [("allowed_extra_keys_with_types", "Extra")]
@@ -627,6 +627,19 @@ definitions:
627
627
  so glean_parser can find it.
628
628
  type: string
629
629
 
630
+ in_session:
631
+ title: In session
632
+ description: |
633
+ Whether this metric participates in session sampling.
634
+ When `true`, the metric is subject to session-level sampling and
635
+ session metadata is attached to it.
636
+ When `false`, the metric bypasses session sampling entirely.
637
+
638
+ Only valid when `type`_ is `event`.
639
+ Defaults to `true` when `type`_ is `event`, `false` otherwise.
640
+ type: boolean
641
+ default: false
642
+
630
643
  structure:
631
644
  title: A subset of a JSON schema definition
632
645
  description: |
@@ -773,6 +786,18 @@ additionalProperties:
773
786
  description: |
774
787
  `denominator_metric` is only allowed for `rate`.
775
788
  maxLength: 0
789
+ - if:
790
+ not:
791
+ properties:
792
+ type:
793
+ const: event
794
+ then:
795
+ properties:
796
+ in_session:
797
+ description: |
798
+ `in_session: true` is only allowed for `event` metrics.
799
+ Only event metrics support session sampling.
800
+ const: false
776
801
  -
777
802
  if:
778
803
  properties:
@@ -1,4 +1,6 @@
1
1
  {# The final Go code is autogenerated, but this template is not. Please file bugs! #}
2
+ {% set include_pubsub = transport in ["pubsub", "combined"] %}
3
+ {% set include_logging = transport in ["logging", "combined"] %}
2
4
  package glean
3
5
 
4
6
  // This Source Code Form is subject to the terms of the Mozilla Public
@@ -9,30 +11,31 @@ package glean
9
11
 
10
12
  // required imports
11
13
  import (
12
- {% if transport == "pubsub" %}
14
+ {% if include_pubsub %}
13
15
  "bytes"
14
16
  "compress/gzip"
17
+ {% endif %}
15
18
  "encoding/json"
16
- "fmt"
17
- "io"
18
- "sync"
19
- "time"
20
-
21
- pubsub "cloud.google.com/go/pubsub/v2"
22
- "github.com/google/uuid"
23
- {% else %}
24
- "encoding/json"
19
+ {% if include_logging %}
25
20
  "errors"
21
+ {% endif %}
26
22
  "fmt"
27
23
  "io"
24
+ {% if include_logging %}
28
25
  "strconv"
26
+ {% endif %}
27
+ {% if include_pubsub %}
28
+ "sync"
29
+ {% endif %}
29
30
  "time"
30
31
 
31
- "github.com/google/uuid"
32
+ {% if include_pubsub %}
33
+ pubsub "cloud.google.com/go/pubsub/v2"
32
34
  {% endif %}
35
+ "github.com/google/uuid"
33
36
  )
34
37
 
35
- {% if transport == "pubsub" %}
38
+ {% if include_pubsub %}
36
39
  // GleanEventsBuilder constructs Pub/Sub messages carrying Glean pings.
37
40
  // Builder is stateless beyond its app-identity fields; callers own the
38
41
  // pubsub.Client, the pubsub.Publisher, batching settings, retries, shutdown sequencing, and any
@@ -74,7 +77,8 @@ func compressPayload(data []byte) ([]byte, error) {
74
77
  gzipPool.Put(gz)
75
78
  return buf.Bytes(), nil
76
79
  }
77
- {% else %}
80
+ {% endif %}
81
+ {% if include_logging %}
78
82
  // log type string used to identify logs to process in the Moz Data Pipeline
79
83
  var gleanEventMozlogType string = "glean-server-event"
80
84
 
@@ -120,7 +124,7 @@ type pingInfo struct {
120
124
  EndTime string `json:"end_time"`
121
125
  }
122
126
 
123
- {% if transport == "logging" %}
127
+ {% if include_logging %}
124
128
  type ping struct {
125
129
  DocumentNamespace string `json:"document_namespace"`
126
130
  DocumentType string `json:"document_type"`
@@ -148,7 +152,7 @@ type gleanEvent struct {
148
152
  Extra map[string]string `json:"extra"`
149
153
  }
150
154
 
151
- {% if transport == "logging" %}
155
+ {% if include_logging %}
152
156
  type logEnvelope struct {
153
157
  Timestamp string
154
158
  Logger string
@@ -157,12 +161,8 @@ type logEnvelope struct {
157
161
  }
158
162
  {% endif %}
159
163
 
160
- {% if transport == "pubsub" %}
161
- func (g GleanEventsBuilder) createClientInfo() clientInfo {
162
- {% else %}
163
- func (g GleanEventsLogger) createClientInfo() clientInfo {
164
- {% endif %}
165
- // Fields with default values are required in the Glean schema, but not used in server context
164
+ // Fields with default values are required in the Glean schema, but not used in server context
165
+ func createClientInfo(appDisplayVersion, appChannel string) clientInfo {
166
166
  return clientInfo{
167
167
  TelemetrySDKBuild: "glean_parser v{{ parser_version }}",
168
168
  FirstRunDate: "Unknown",
@@ -170,8 +170,8 @@ func (g GleanEventsLogger) createClientInfo() clientInfo {
170
170
  OSVersion: "Unknown",
171
171
  Architecture: "Unknown",
172
172
  AppBuild: "Unknown",
173
- AppDisplayVersion: g.AppDisplayVersion,
174
- AppChannel: g.AppChannel,
173
+ AppDisplayVersion: appDisplayVersion,
174
+ AppChannel: appChannel,
175
175
  }
176
176
  }
177
177
 
@@ -185,14 +185,23 @@ func createPingInfo() pingInfo {
185
185
  }
186
186
  }
187
187
 
188
- {% if transport == "logging" %}
189
- func (g GleanEventsLogger) createPing(documentType string, config RequestInfo, payload pingPayload) (ping, error) {
190
- payloadJson, err := json.Marshal(payload)
188
+ // resolveDocumentID returns supplied if non-empty, otherwise a fresh UUID.
189
+ // Set DocumentID on the ping params struct to share an ID across transports
190
+ // (e.g., dual-write verification during a logging->pubsub migration).
191
+ func resolveDocumentID(supplied string) (string, error) {
192
+ if supplied != "" {
193
+ return supplied, nil
194
+ }
195
+ id, err := uuid.NewRandom()
191
196
  if err != nil {
192
- return ping{}, err
197
+ return "", err
193
198
  }
199
+ return id.String(), nil
200
+ }
194
201
 
195
- documentID, err := uuid.NewRandom()
202
+ {% if include_logging %}
203
+ func (g GleanEventsLogger) createPing(documentType, documentID string, config RequestInfo, payload pingPayload) (ping, error) {
204
+ payloadJson, err := json.Marshal(payload)
196
205
  if err != nil {
197
206
  return ping{}, err
198
207
  }
@@ -201,7 +210,7 @@ func (g GleanEventsLogger) createPing(documentType string, config RequestInfo, p
201
210
  DocumentNamespace: g.AppID,
202
211
  DocumentType: documentType,
203
212
  DocumentVersion: "1",
204
- DocumentID: documentID.String(),
213
+ DocumentID: documentID,
205
214
  UserAgent: config.UserAgent,
206
215
  IpAddress: config.IpAddress,
207
216
  Payload: string(payloadJson),
@@ -209,25 +218,18 @@ func (g GleanEventsLogger) createPing(documentType string, config RequestInfo, p
209
218
  }
210
219
 
211
220
  // method called by each ping-specific record method.
212
- // construct the ping, wrap it in the envelope, and print to stdout
221
+ // wrap the payload in the envelope and print to stdout
213
222
  func (g GleanEventsLogger) record(
214
223
  documentType string,
224
+ documentID string,
215
225
  requestInfo RequestInfo,
216
- metrics metrics,
217
- events []gleanEvent,
226
+ payload pingPayload,
218
227
  ) error {
219
228
  if g.Writer == nil {
220
229
  return errors.New("writer not specified")
221
230
  }
222
231
 
223
- telemetryPayload := pingPayload{
224
- ClientInfo: g.createClientInfo(),
225
- PingInfo: createPingInfo(),
226
- Metrics: metrics,
227
- Events: events,
228
- }
229
-
230
- ping, err := g.createPing(documentType, requestInfo, telemetryPayload)
232
+ ping, err := g.createPing(documentType, documentID, requestInfo, payload)
231
233
  if err != nil {
232
234
  return err
233
235
  }
@@ -330,25 +332,17 @@ type {{ ping|ping_type_name }} struct {
330
332
  {% if metrics_by_type['event'] %}
331
333
  Event {{ ping|ping_events_type_name }} // valid event for this ping
332
334
  {% endif %}
335
+ DocumentID string // optional; used as document_id if non-empty, otherwise a fresh UUID is generated
333
336
  }
334
337
 
335
- {% if transport == "pubsub" %}
336
- // Build{{ ping|ping_type_name }}Message constructs a Pub/Sub message carrying
337
- // the given `{{ ping }}` ping. The caller publishes the returned message
338
- // (e.g., topic.Publish(ctx, msg)) and owns batching, retries, shutdown
339
- // sequencing, and any publish-result metrics.
340
- //
341
- // Wire-format contract: see
342
- // docs/architecture/decoder_service_specification.md in mozilla/gcp-ingestion.
343
- func (g GleanEventsBuilder) Build{{ ping|ping_type_name }}Message(
344
- requestInfo RequestInfo,
345
- params {{ ping|ping_type_name }},
346
- ) (*pubsub.Message, error) {
338
+ // buildPayload assembles the inner pingPayload for `{{ ping }}`. Shared by
339
+ // the logging and pub/sub code paths.
340
+ func (p {{ ping|ping_type_name }}) buildPayload(appDisplayVersion, appChannel string) pingPayload {
347
341
  {% if metrics_by_type['string_list'] %}
348
342
  {% for metric in metrics_by_type['string_list'] %}
349
343
  // Ensure nil string_list metrics serialize as empty arrays, not null
350
- if params.{{ metric|metric_argument_name }} == nil {
351
- params.{{ metric|metric_argument_name }} = []string{}
344
+ if p.{{ metric|metric_argument_name }} == nil {
345
+ p.{{ metric|metric_argument_name }} = []string{}
352
346
  }
353
347
  {% endfor %}
354
348
  {% endif %}
@@ -358,9 +352,9 @@ func (g GleanEventsBuilder) Build{{ ping|ping_type_name }}Message(
358
352
  "{{ metric_type }}": {
359
353
  {% for metric in metrics %}
360
354
  {% if metric_type == 'datetime' %}
361
- "{{ metric|metric_name }}": params.{{ metric|metric_argument_name }}.Format("2006-01-02T15:04:05.000Z"),
355
+ "{{ metric|metric_name }}": p.{{ metric|metric_argument_name }}.Format("2006-01-02T15:04:05.000Z"),
362
356
  {% else %}
363
- "{{ metric|metric_name }}": params.{{ metric|metric_argument_name }},
357
+ "{{ metric|metric_name }}": p.{{ metric|metric_argument_name }},
364
358
  {% endif %}
365
359
  {% endfor %}
366
360
  },
@@ -370,17 +364,32 @@ func (g GleanEventsBuilder) Build{{ ping|ping_type_name }}Message(
370
364
 
371
365
  events := []gleanEvent{}
372
366
  {% if metrics_by_type['event'] %}
373
- if params.Event != nil {
374
- events = append(events, params.Event.gleanEvent())
367
+ if p.Event != nil {
368
+ events = append(events, p.Event.gleanEvent())
375
369
  }
376
370
  {% endif %}
377
371
 
378
- payload := pingPayload{
379
- ClientInfo: g.createClientInfo(),
372
+ return pingPayload{
373
+ ClientInfo: createClientInfo(appDisplayVersion, appChannel),
380
374
  PingInfo: createPingInfo(),
381
375
  Metrics: metrics,
382
376
  Events: events,
383
377
  }
378
+ }
379
+
380
+ {% if include_pubsub %}
381
+ // Build{{ ping|ping_type_name }}Message constructs a Pub/Sub message carrying
382
+ // the given `{{ ping }}` ping. The caller publishes the returned message
383
+ // (e.g., topic.Publish(ctx, msg)) and owns batching, retries, shutdown
384
+ // sequencing, and any publish-result metrics.
385
+ //
386
+ // Wire-format contract: see
387
+ // docs/architecture/decoder_service_specification.md in mozilla/gcp-ingestion.
388
+ func (g GleanEventsBuilder) Build{{ ping|ping_type_name }}Message(
389
+ requestInfo RequestInfo,
390
+ params {{ ping|ping_type_name }},
391
+ ) (*pubsub.Message, error) {
392
+ payload := params.buildPayload(g.AppDisplayVersion, g.AppChannel)
384
393
 
385
394
  payloadJSON, err := json.Marshal(payload)
386
395
  if err != nil {
@@ -391,16 +400,16 @@ func (g GleanEventsBuilder) Build{{ ping|ping_type_name }}Message(
391
400
  return nil, fmt.Errorf("compress payload: %w", err)
392
401
  }
393
402
 
394
- documentID, err := uuid.NewRandom()
403
+ documentID, err := resolveDocumentID(params.DocumentID)
395
404
  if err != nil {
396
- return nil, fmt.Errorf("generate document_id: %w", err)
405
+ return nil, fmt.Errorf("resolve document_id: %w", err)
397
406
  }
398
407
 
399
408
  attributes := map[string]string{
400
409
  "document_namespace": g.AppID,
401
410
  "document_type": "{{ ping }}",
402
411
  "document_version": "1",
403
- "document_id": documentID.String(),
412
+ "document_id": documentID,
404
413
  }
405
414
  // Skip empty optional attributes; the decoder treats missing and empty
406
415
  // the same, and Pub/Sub charges for attribute bytes. The publisher does
@@ -426,43 +435,19 @@ func (g GleanEventsBuilder) Build{{ ping|ping_type_name }}MessageWithoutUserInfo
426
435
  ) (*pubsub.Message, error) {
427
436
  return g.Build{{ ping|ping_type_name }}Message(defaultRequestInfo, params)
428
437
  }
429
- {% else %}
438
+ {% endif %}
439
+ {% if include_logging %}
430
440
  // Record and submit `{{ ping }}` ping
431
441
  func (g GleanEventsLogger) Record{{ ping|ping_type_name }}(
432
442
  requestInfo RequestInfo,
433
443
  params {{ ping|ping_type_name }},
434
444
  ) error {
435
- {% if metrics_by_type['string_list'] %}
436
- {% for metric in metrics_by_type['string_list'] %}
437
- // Ensure nil string_list metrics serialize as empty arrays, not null
438
- if params.{{ metric|metric_argument_name }} == nil {
439
- params.{{ metric|metric_argument_name }} = []string{}
440
- }
441
- {% endfor %}
442
- {% endif %}
443
- metrics := metrics{
444
- {% for metric_type, metrics in metrics_by_type.items() %}
445
- {% if metric_type != 'event' %}
446
- "{{ metric_type }}": {
447
- {% for metric in metrics %}
448
- {% if metric_type == 'datetime' %}
449
- "{{ metric|metric_name }}": params.{{ metric|metric_argument_name }}.Format("2006-01-02T15:04:05.000Z"),
450
- {% else %}
451
- "{{ metric|metric_name }}": params.{{ metric|metric_argument_name }},
452
- {% endif %}
453
- {% endfor %}
454
- },
455
- {% endif %}
456
- {% endfor %}
457
- }
458
-
459
- events := []gleanEvent{}
460
- {% if metrics_by_type['event'] %}
461
- if params.Event != nil {
462
- events = append(events, params.Event.gleanEvent())
445
+ documentID, err := resolveDocumentID(params.DocumentID)
446
+ if err != nil {
447
+ return fmt.Errorf("resolve document_id: %w", err)
463
448
  }
464
- {% endif %}
465
- return g.record("{{ ping }}", requestInfo, metrics, events)
449
+ payload := params.buildPayload(g.AppDisplayVersion, g.AppChannel)
450
+ return g.record("{{ ping }}", documentID, requestInfo, payload)
466
451
  }
467
452
 
468
453
  // Record and submit `{{ ping }}` ping omitting user request info
@@ -102,6 +102,7 @@ CommonMetricData {
102
102
  send_in_pings: {{ obj.send_in_pings|rust }},
103
103
  lifetime: {{ obj.lifetime|rust }},
104
104
  disabled: {{ obj.is_disabled()|rust }},
105
+ in_session: {{ obj.in_session|rust }},
105
106
  ..Default::default()
106
107
  }
107
108
  {%- endmacro -%}
@@ -58,7 +58,6 @@ class Outputter:
58
58
 
59
59
  OUTPUTTERS = {
60
60
  "go_server": Outputter(go_server.output_go_logger, []),
61
- "go_server_pubsub": Outputter(go_server.output_go_pubsub, []),
62
61
  "javascript": Outputter(javascript.output_javascript, []),
63
62
  "typescript": Outputter(javascript.output_typescript, []),
64
63
  "javascript_server": Outputter(javascript_server.output_javascript, []),
@@ -45,6 +45,11 @@ JavaScript:
45
45
  Markdown:
46
46
  - `project_title`: The project's title.
47
47
 
48
+ Go server (`go_server`):
49
+ - `transport`: Transport mode. One of `logging` (Cloud Logging, the default),
50
+ `pubsub` (Pub/Sub direct publishing), or `combined` (emit both in a
51
+ single file, sharing common types, for a gradual migration).
52
+
48
53
  (press q to exit)"""
49
54
 
50
55
  if value:
@@ -515,6 +515,7 @@ common_metric_args = [
515
515
  "send_in_pings",
516
516
  "lifetime",
517
517
  "disabled",
518
+ "in_session",
518
519
  ]
519
520
 
520
521
 
@@ -107,6 +107,7 @@ all_metrics:
107
107
  event:
108
108
  <<: *defaults
109
109
  type: event
110
+ in_session: true
110
111
  extra_keys:
111
112
  source:
112
113
  description: Source of this event