Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metrics table generation from yaml #79

Merged
merged 59 commits into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
c1ccabd
functionality for metrics table generation from yaml
jamesmoessis Jan 11, 2022
40d73ef
tests for metric convention yaml->markdown
jamesmoessis Jan 12, 2022
beead27
appease linter
jamesmoessis Jan 12, 2022
6e4ff11
use zip instead of enumerate
jamesmoessis Jan 25, 2022
cd9d815
raise error if metric_table is specified on non-metric semconv
jamesmoessis Jan 25, 2022
f4aef51
update syntax.md with initial metrics syntax
jamesmoessis Jan 25, 2022
2421f0b
remove duplicated code and files
jamesmoessis Jan 25, 2022
5ac5895
add extra_yaml_files param and use it in metric_table test
jamesmoessis Jan 25, 2022
bec1a1b
use 'FQN = prefix + id' for metric FQNs
jamesmoessis Jan 27, 2022
22373cd
change InstrumentKind enum to ALL_CAPS and remove sync/async distinction
jamesmoessis Apr 13, 2022
ec98643
implement syntax review and semantics docs
jamesmoessis Apr 13, 2022
9bcc6e4
raise error from different position to avoid re-raising
jamesmoessis Apr 13, 2022
87dfe5d
remove markdown link
jamesmoessis Apr 13, 2022
6df71da
implement various review items
jamesmoessis Apr 14, 2022
a96e962
Merge branch 'main' into metrics-semconv
jamesmoessis Apr 14, 2022
93eb443
upgrade black version to try solve build failure
jamesmoessis Apr 14, 2022
a77142c
appease linter
jamesmoessis Apr 14, 2022
c50b9e0
fix typo
jamesmoessis Apr 14, 2022
13316cd
stop using enum for instrument kind
jamesmoessis Apr 26, 2022
e01f296
extract attribute table writing to own function
jamesmoessis Apr 26, 2022
5fbdcf9
appease linter
jamesmoessis Apr 26, 2022
a3b7e7f
add link to UCUM
jamesmoessis Apr 26, 2022
8ec68ed
consistent camelcase syntax
jamesmoessis Apr 26, 2022
e6d4da2
Merge branch 'main' into metrics-semconv
jamesmoessis Jun 6, 2022
dfe7b0e
Address reyang comments
jamesmoessis Jun 6, 2022
cde4200
update tests so they pass with latest main
jamesmoessis Jun 6, 2022
9217e78
move logic of stripping metric. prefix
jamesmoessis Jun 6, 2022
9178ab3
appease linter
jamesmoessis Jun 6, 2022
a012741
remove 'metric.' prefix so we don't have to removeprefix() when seria…
jamesmoessis Jun 6, 2022
b06e586
Merge branch 'main' into metrics-semconv
jamesmoessis Sep 8, 2022
f028520
make test match current changed expectation for requirement level
jamesmoessis Sep 9, 2022
68b1154
new yaml structure for metrics
jamesmoessis Sep 9, 2022
849c376
change the structure of yaml for metrics
jamesmoessis Sep 9, 2022
fd33c98
foo bar test for new metric structure
jamesmoessis Sep 9, 2022
6536fb8
alter syntax definition in readme
jamesmoessis Sep 9, 2022
9e7e718
simplify test slightly
jamesmoessis Sep 9, 2022
f6f1d5a
implement several review items
jamesmoessis Sep 13, 2022
3f3a104
units -> unit in some places I missed before
jamesmoessis Sep 13, 2022
f2f64b6
newline for readability
jamesmoessis Sep 13, 2022
766cc5d
remove unneeded yaml definition
jamesmoessis Sep 13, 2022
f1f7e73
add JSON schema definition for MetricSemanticConvention
jamesmoessis Sep 13, 2022
167ba00
add changelog
jamesmoessis Sep 13, 2022
5c38093
appease linter
jamesmoessis Sep 13, 2022
8fb5af0
address comment regarding instrument types
jamesmoessis Sep 16, 2022
3a775d3
update docs on metric_table semconv generator example
jamesmoessis Oct 5, 2022
d237bf2
restore event name in syntax.md which erroneously removed
jamesmoessis Oct 19, 2022
e8259dd
add more metrics/attributes to test, clarifying intentions
jamesmoessis Oct 19, 2022
6a67c74
use different prefix in tests for clarity
jamesmoessis Oct 24, 2022
e524ab4
differentiate parent and concrete metric types with different semanti…
jamesmoessis Oct 24, 2022
4a8aabd
update syntax.md and JSON schema for metric_group addition
jamesmoessis Oct 26, 2022
9943b8f
address comment regarding wording in changelog
jamesmoessis Nov 29, 2022
cf47089
remove unnecessary 'extends' from test case
jamesmoessis Nov 30, 2022
086b616
address comments about clarifying and rewording definitions in syntax.md
jamesmoessis Nov 30, 2022
9da7198
remove field that stores instrument markdown repr, instead calculate …
jamesmoessis Nov 30, 2022
cff3949
appease lint
jamesmoessis Nov 30, 2022
57d8c4f
Merge branch 'main' into metrics-semconv
jamesmoessis Nov 30, 2022
fdd98f3
appease new linter f-string requirements
jamesmoessis Dec 1, 2022
0f086b0
use f-string and inline a variable
jamesmoessis Dec 6, 2022
f393d8b
Merge branch 'main' into metrics-semconv
jamesmoessis Jan 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion semantic-conventions/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Please update the changelog as part of any significant pull request.

## Unreleased

- ...
- Add definitions for metrics in YAML semconv, plus generation to markdown
jamesmoessis marked this conversation as resolved.
Show resolved Hide resolved
([#79](https://github.com/open-telemetry/build-tools/pull/79))

## v0.14.0

Expand Down
3 changes: 3 additions & 0 deletions semantic-conventions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ convention that have the tag `network`.
`<!-- semconv http.server(tag=network, full) -->` will print the constraints and attributes of both `http` and `http.server`
semantic conventions that have the tag `network`.

`<!-- semconv metric.http.server.active_requests(metric_table) -->` will print a table describing a single metric
`http.server.active_requests`.

## Code Generator

The image supports [Jinja](https://jinja.palletsprojects.com/en/2.11.x/) templates to generate code from the models.
Expand Down
47 changes: 47 additions & 0 deletions semantic-conventions/semconv.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
},
{
"allOf": [{"$ref": "#/definitions/EventSemanticConvention"}]
},
{
"allOf": [{"$ref": "#/definitions/MetricSemanticConvention"}]
}
]
}
Expand Down Expand Up @@ -51,6 +54,7 @@
"span",
"resource",
"metric",
"metric_group",
"event",
"scope"
],
Expand Down Expand Up @@ -164,6 +168,49 @@
{"required": ["name"]}
]
},
"MetricGroupSemanticConvention": {
"allOf": [{ "$ref": "#/definitions/SemanticConventionBase" }],
"required": ["type"],
"properties": {
"type": {
"type": "string",
"const": "metric_group"
}
}
},
"MetricSemanticConvention": {
"allOf": [{ "$ref": "#/definitions/SemanticConventionBase" }],
"required": [
"type",
"metric_name",
"instrument",
"unit"
],
"properties": {
"instrument": {
"type": "string",
"description": "The instrument used to record the metric.",
"enum": [
"counter",
"gauge",
"histogram",
"updowncounter"
]
},
"metric_name": {
"type": "string",
"description": "The name of the metric."
},
"type": {
"type": "string",
"const": "metric"
},
"unit": {
"type": "string",
"description": "The unit in which the metric is measured in."
}
}
},
"SemanticConvention": {
"allOf": [{ "$ref": "#/definitions/SemanticConventionBase" }],
"required": ["type"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import typing
from dataclasses import dataclass, field
from enum import Enum
from typing import Tuple, Union
from typing import Dict, Tuple, Union

from ruamel.yaml import YAML

Expand Down Expand Up @@ -244,10 +244,55 @@ def __init__(self, group):
self.members = UnitMember.parse(group.get("members"))


class MetricSemanticConvention(BaseSemanticConvention):
class MetricGroupSemanticConvention(BaseSemanticConvention):
GROUP_TYPE_NAME = "metric_group"
arminru marked this conversation as resolved.
Show resolved Hide resolved


class MetricSemanticConvention(MetricGroupSemanticConvention):
GROUP_TYPE_NAME = "metric"

allowed_keys = ()
allowed_keys: Tuple[str, ...] = BaseSemanticConvention.allowed_keys + (
"metric_name",
"unit",
"instrument",
)

yaml_to_markdown_instrument_repr: Dict[str, str] = {
Oberon00 marked this conversation as resolved.
Show resolved Hide resolved
"counter": "Counter",
"updowncounter": "UpDownCounter",
"histogram": "Histogram",
jamesmoessis marked this conversation as resolved.
Show resolved Hide resolved
"gauge": "Gauge",
}

allowed_instruments: Tuple[str, ...] = tuple(
yaml_to_markdown_instrument_repr.keys()
)

def __init__(self, group):
super().__init__(group)
self.metric_name = group.get("metric_name")
self.unit = group.get("unit")
self.instrument = group.get("instrument")
self.instrument_markdown_fmt = self.yaml_to_markdown_instrument_repr.get(
jamesmoessis marked this conversation as resolved.
Show resolved Hide resolved
self.instrument
)
self.validate()

def validate(self):
val_tuple = (self.metric_name, self.unit, self.instrument)
if not all(val_tuple):
raise ValidationError.from_yaml_pos(
self._position,
"All of metric_name, units, and instrument must be defined",
)

if self.instrument not in self.allowed_instruments:
raise ValidationError.from_yaml_pos(
self._position,
"Instrument '{}' is not a valid instrument name".format(
self.instrument
),
)


@dataclass
Expand Down Expand Up @@ -543,6 +588,7 @@ def attributes(self):
SpanSemanticConvention,
ResourceSemanticConvention,
EventSemanticConvention,
MetricGroupSemanticConvention,
MetricSemanticConvention,
UnitSemanticConvention,
ScopeSemanticConvention,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
StabilityLevel,
)
from opentelemetry.semconv.model.semantic_convention import (
BaseSemanticConvention,
EventSemanticConvention,
MetricSemanticConvention,
SemanticConventionSet,
UnitSemanticConvention,
)
Expand All @@ -41,6 +43,7 @@ class RenderContext:
def __init__(self):
self.is_full = False
self.is_remove_constraint = False
self.is_metric_table = False
self.group_key = ""
self.enums = []
self.notes = []
Expand All @@ -66,7 +69,7 @@ class MarkdownRenderer:
)
p_end = re.compile("<!--\\s*endsemconv\\s*-->")
default_break_conditional_labels = 50
valid_parameters = ["tag", "full", "remove_constraints"]
valid_parameters = ["tag", "full", "remove_constraints", "metric_table"]

prelude = "<!-- semconv {} -->\n"
table_headers = "| Attribute | Type | Description | Examples | Requirement Level |\n|---|---|---|---|---|\n"
Expand Down Expand Up @@ -186,6 +189,61 @@ def to_markdown_attr(
)
)

def to_markdown_attribute_table(
jamesmoessis marked this conversation as resolved.
Show resolved Hide resolved
self, semconv: BaseSemanticConvention, output: io.StringIO
):
attr_to_print = []
for attr in sorted(
semconv.attributes, key=lambda a: "" if a.ref is None else a.ref
):
if self.render_ctx.group_key is not None:
if attr.tag == self.render_ctx.group_key:
attr_to_print.append(attr)
continue
if self.render_ctx.is_full or attr.is_local:
attr_to_print.append(attr)
if self.render_ctx.group_key is not None and not attr_to_print:
raise ValueError(
"No attributes retained for '{}' filtering by '{}'".format(
semconv.semconv_id, self.render_ctx.group_key
)
)
if attr_to_print:
output.write(MarkdownRenderer.table_headers)
for attr in attr_to_print:
self.to_markdown_attr(attr, output)
attr_sampling_relevant = [
attr for attr in attr_to_print if attr.sampling_relevant
]
self.to_creation_time_attributes(attr_sampling_relevant, output)
Oberon00 marked this conversation as resolved.
Show resolved Hide resolved

@staticmethod
def to_markdown_metric_table(
semconv: MetricSemanticConvention, output: io.StringIO
):
"""
This method renders metrics as markdown table entry
"""
if not isinstance(semconv, MetricSemanticConvention):
raise ValueError(
"semconv `{}` was specified with `metric_table`, but it is not a metric convention".format(
semconv.semconv_id
)
)

output.write(
"| Name | Instrument Type | Unit (UCUM) | Description |\n"
jamesmoessis marked this conversation as resolved.
Show resolved Hide resolved
"| -------- | --------------- | ----------- | -------------- |\n"
)
output.write(
"| `{}` | {} | `{}` | {} |\n".format(
semconv.metric_name,
semconv.instrument_markdown_fmt,
semconv.unit,
semconv.brief,
)
)

def to_markdown_anyof(self, anyof: AnyOf, output: io.StringIO):
"""
This method renders anyof constraints into markdown lists
Expand Down Expand Up @@ -428,31 +486,15 @@ def _render_group(self, semconv, parameters, output):
self.render_ctx.is_remove_constraint = "remove_constraints" in parameters
self.render_ctx.group_key = parameters.get("tag")
self.render_ctx.is_full = "full" in parameters
self.render_ctx.is_metric_table = "metric_table" in parameters

if isinstance(semconv, EventSemanticConvention):
output.write("The event name MUST be `{}`.\n\n".format(semconv.name))
if self.render_ctx.is_metric_table:
self.to_markdown_metric_table(semconv, output)
else:
if isinstance(semconv, EventSemanticConvention):
output.write("The event name MUST be `{}`.\n\n".format(semconv.name))
self.to_markdown_attribute_table(semconv, output)

attr_to_print = []
attr: SemanticAttribute
for attr in sorted(
semconv.attributes, key=lambda a: "" if a.ref is None else a.ref
):
if self.render_ctx.group_key is not None:
if attr.tag == self.render_ctx.group_key:
attr_to_print.append(attr)
continue
if self.render_ctx.is_full or attr.is_local:
attr_to_print.append(attr)
if self.render_ctx.group_key is not None and not attr_to_print:
raise ValueError(
"No attributes retained for '{}' filtering by '{}'".format(
semconv.semconv_id, self.render_ctx.group_key
)
)
if attr_to_print:
output.write(MarkdownRenderer.table_headers)
for attr in attr_to_print:
self.to_markdown_attr(attr, output)
self.to_markdown_notes(output)
if not self.render_ctx.is_remove_constraint:
for cnst in semconv.constraints:
Expand All @@ -462,9 +504,4 @@ def _render_group(self, semconv, parameters, output):
if isinstance(semconv, UnitSemanticConvention):
self.to_markdown_unit_table(semconv.members, output)

attr_sampling_relevant = [
attr for attr in attr_to_print if attr.sampling_relevant
]
self.to_creation_time_attributes(attr_sampling_relevant, output)

output.write("<!-- endsemconv -->")
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Test Markdown

**`foo.size`**
<!-- semconv metric.foo.size(metric_table) -->
| Name | Instrument Type | Unit (UCUM) | Description |
| -------- | --------------- | ----------- | -------------- |
| `foo.size` | Histogram | `{bars}` | Measures the size of foo. |
<!-- endsemconv -->
arminru marked this conversation as resolved.
Show resolved Hide resolved

**Attributes for `foo.size`**
jamesmoessis marked this conversation as resolved.
Show resolved Hide resolved
<!-- semconv metric.foo.size -->
| Attribute | Type | Description | Examples | Requirement Level |
|---|---|---|---|---|
| `http.method` | string | HTTP request method. | `GET`; `POST`; `HEAD` | Required |
| `http.status_code` | int | [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). | `200` | Conditionally Required: if and only if one was received/sent. |
<!-- endsemconv -->

**`foo.active_eggs`**
<!-- semconv metric.foo.active_eggs(metric_table) -->
| Name | Instrument Type | Unit (UCUM) | Description |
| -------- | --------------- | ----------- | -------------- |
| `foo.active_eggs` | UpDownCounter | `{cartons}` | Measures how many eggs are currently active. |
<!-- endsemconv -->

**Attributes for `foo.active_eggs`**
<!-- semconv metric.foo.active_eggs -->
| Attribute | Type | Description | Examples | Requirement Level |
|---|---|---|---|---|
| `bar.egg.type` | string | Type of egg. | `chicken`; `emu`; `dragon` | Conditionally Required: if available to instrumentation. |
| `http.method` | string | HTTP request method. | `GET`; `POST`; `HEAD` | Optional |
<!-- endsemconv -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Test Markdown

**`foo.size`**
<!-- semconv metric.foo.size(metric_table) -->
<!-- endsemconv -->

**Attributes for `foo.size`**
<!-- semconv metric.foo.size -->
<!-- endsemconv -->

**`foo.active_eggs`**
<!-- semconv metric.foo.active_eggs(metric_table) -->
<!-- endsemconv -->

**Attributes for `foo.active_eggs`**
<!-- semconv metric.foo.active_eggs -->
<!-- endsemconv -->
42 changes: 42 additions & 0 deletions semantic-conventions/src/tests/data/yaml/metrics.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
groups:
- id: metric.foo
prefix: bar
type: metric_group
brief: "This document defines foo."
note: >
Details about foo.
attributes:
- id: egg.type
type: string
brief: 'Type of egg.'
examples: ["chicken", "emu", "dragon"]

- id: metric.foo.size
prefix: foo
type: metric
extends: metric.foo
jamesmoessis marked this conversation as resolved.
Show resolved Hide resolved
metric_name: foo.size
jamesmoessis marked this conversation as resolved.
Show resolved Hide resolved
brief: "Measures the size of foo."
instrument: histogram
unit: "{bars}"
attributes:
- ref: http.method
jamesmoessis marked this conversation as resolved.
Show resolved Hide resolved
requirement_level: required
- ref: http.status_code
requirement_level:
conditionally_required: "if and only if one was received/sent."

- id: metric.foo.active_eggs
prefix: foo
type: metric
extends: metric.foo
metric_name: foo.active_eggs
brief: "Measures how many eggs are currently active."
instrument: updowncounter
unit: "{cartons}"
attributes:
- ref: http.method
requirement_level: optional
- ref: bar.egg.type
requirement_level:
conditionally_required: "if available to instrumentation."