Skip to content

Commit 44ba726

Browse files
authored
Merge pull request #76 from autometrics-dev/add-service-name
Add service.name
2 parents 38379b3 + acacec4 commit 44ba726

10 files changed

+107
-49
lines changed

Diff for: CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2222
- When the `function.calls.duration` histogram is exported to Prometheus, it now
2323
includes the units (`function_calls_duration_seconds`) to be in line with
2424
Prometheus/OpenMetrics naming conventions. **Dashboards and alerting rules must be updated.**
25+
- The `caller` label on the `function.calls` metric was replaced with `caller.function`
26+
and `caller.module`
27+
- All metrics now have a `service.name` label attached. This is set via runtime environment
28+
variable (`AUTOMETRICS_SERVICE_NAME` or `OTEL_SERVICE_NAME`), or falls back to the package name.
2529

2630
### Deprecated
2731

Diff for: README.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def api_handler():
8989

9090
Autometrics keeps track of instrumented functions calling each other. If you have a function that calls another function, metrics for later will include `caller` label set to the name of the autometricised function that called it.
9191

92-
#### Metrics Libraries
92+
## Metrics Libraries
9393

9494
Configure the package that autometrics will use to produce metrics with the `AUTOMETRICS_TRACKER` environment variable.
9595

@@ -116,6 +116,17 @@ Configure the labels by setting the following environment variables:
116116

117117
This follows the method outlined in [Exposing the software version to Prometheus](https://www.robustperception.io/exposing-the-software-version-to-prometheus/).
118118

119+
## Service name
120+
121+
All metrics produced by Autometrics have a label called `service.name` (or `service_name` when exported to Prometheus) attached to identify the logical service they are part of.
122+
123+
You may want to override the default service name, for example if you are running multiple instances of the same code base as separate services and want to differentiate between the metrics produced by each one.
124+
125+
The service name is loaded from the following environment variables, in this order:
126+
127+
1. `AUTOMETRICS_SERVICE_NAME` (at runtime)
128+
2. `OTEL_SERVICE_NAME` (at runtime)
129+
119130
## Exemplars
120131

121132
> **NOTE** - As of writing, exemplars aren't supported by the default tracker (`AUTOMETRICS_TRACKER=OPEN_TELEMETRY`).

Diff for: src/autometrics/constants.py

+2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
CONCURRENCY_NAME = "function.calls.concurrent"
66
# NOTE - The Rust implementation does not use `build.info`, instead opts for just `build_info`
77
BUILD_INFO_NAME = "build_info"
8+
SERVICE_NAME = "service.name"
89

910

1011
COUNTER_NAME_PROMETHEUS = COUNTER_NAME.replace(".", "_")
1112
HISTOGRAM_NAME_PROMETHEUS = HISTOGRAM_NAME.replace(".", "_")
1213
CONCURRENCY_NAME_PROMETHEUS = CONCURRENCY_NAME.replace(".", "_")
14+
SERVICE_NAME_PROMETHEUS = SERVICE_NAME.replace(".", "_")
1315

1416
COUNTER_DESCRIPTION = "Autometrics counter for tracking function calls"
1517
HISTOGRAM_DESCRIPTION = "Autometrics histogram for tracking function call duration"

Diff for: src/autometrics/test_caller.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@ def bar():
3838
assert blob is not None
3939
data = blob.decode("utf-8")
4040

41-
expected = """function_calls_total{caller_function="test_caller_detection.<locals>.bar",caller_module="autometrics.test_caller",function="test_caller_detection.<locals>.foo",module="autometrics.test_caller",objective_name="",objective_percentile="",result="ok"} 1.0"""
41+
expected = """function_calls_total{caller_function="test_caller_detection.<locals>.bar",caller_module="autometrics.test_caller",function="test_caller_detection.<locals>.foo",module="autometrics.test_caller",objective_name="",objective_percentile="",result="ok",service_name="autometrics"} 1.0"""
4242
assert "wrapper" not in data
4343
assert expected in data

Diff for: src/autometrics/test_decorator.py

+32-32
Large diffs are not rendered by default.

Diff for: src/autometrics/tracker/opentelemetry.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424
HISTOGRAM_NAME,
2525
BUILD_INFO_NAME,
2626
BUILD_INFO_DESCRIPTION,
27+
SERVICE_NAME,
2728
OBJECTIVE_NAME,
2829
OBJECTIVE_PERCENTILE,
2930
OBJECTIVE_LATENCY_THRESHOLD,
3031
)
31-
32-
FUNCTION_CALLS_DURATION_NAME = "function.calls.duration"
32+
from ..utils import get_service_name
3333

3434

3535
def get_objective_boundaries():
@@ -103,6 +103,7 @@ def __count(
103103
"caller.function": caller_function,
104104
OBJECTIVE_NAME: objective_name,
105105
OBJECTIVE_PERCENTILE: percentile,
106+
SERVICE_NAME: get_service_name(),
106107
},
107108
)
108109

@@ -130,6 +131,7 @@ def __histogram(
130131
attributes={
131132
"function": function,
132133
"module": module,
134+
SERVICE_NAME: get_service_name(),
133135
OBJECTIVE_NAME: objective_name,
134136
OBJECTIVE_PERCENTILE: percentile,
135137
OBJECTIVE_LATENCY_THRESHOLD: threshold,
@@ -145,11 +147,15 @@ def set_build_info(self, commit: str, version: str, branch: str):
145147
"commit": commit,
146148
"version": version,
147149
"branch": branch,
150+
SERVICE_NAME: get_service_name(),
148151
},
149152
)
150153

151154
def start(
152-
self, function: str, module: str, track_concurrency: Optional[bool] = False
155+
self,
156+
function: str,
157+
module: str,
158+
track_concurrency: Optional[bool] = False,
153159
):
154160
"""Start tracking metrics for a function call."""
155161
if track_concurrency:
@@ -158,6 +164,7 @@ def start(
158164
attributes={
159165
"function": function,
160166
"module": module,
167+
SERVICE_NAME: get_service_name(),
161168
},
162169
)
163170

@@ -195,6 +202,7 @@ def finish(
195202
attributes={
196203
"function": function,
197204
"module": module,
205+
SERVICE_NAME: get_service_name(),
198206
},
199207
)
200208

Diff for: src/autometrics/tracker/prometheus.py

+26-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
COUNTER_NAME_PROMETHEUS,
88
HISTOGRAM_NAME_PROMETHEUS,
99
CONCURRENCY_NAME_PROMETHEUS,
10+
SERVICE_NAME_PROMETHEUS,
1011
BUILD_INFO_NAME,
1112
COUNTER_DESCRIPTION,
1213
HISTOGRAM_DESCRIPTION,
@@ -23,6 +24,7 @@
2324
from .exemplar import get_exemplar
2425
from .tracker import Result
2526
from ..objectives import Objective
27+
from ..utils import get_service_name
2628

2729

2830
class PrometheusTracker:
@@ -34,6 +36,7 @@ class PrometheusTracker:
3436
[
3537
"function",
3638
"module",
39+
SERVICE_NAME_PROMETHEUS,
3740
"result",
3841
"caller_module",
3942
"caller_function",
@@ -47,17 +50,26 @@ class PrometheusTracker:
4750
[
4851
"function",
4952
"module",
53+
SERVICE_NAME_PROMETHEUS,
5054
OBJECTIVE_NAME_PROMETHEUS,
5155
OBJECTIVE_PERCENTILE_PROMETHEUS,
5256
OBJECTIVE_LATENCY_THRESHOLD_PROMETHEUS,
5357
],
5458
unit="seconds",
5559
)
5660
prom_gauge_build_info = Gauge(
57-
BUILD_INFO_NAME, BUILD_INFO_DESCRIPTION, [COMMIT_KEY, VERSION_KEY, BRANCH_KEY]
61+
BUILD_INFO_NAME,
62+
BUILD_INFO_DESCRIPTION,
63+
[COMMIT_KEY, VERSION_KEY, BRANCH_KEY, SERVICE_NAME_PROMETHEUS],
5864
)
5965
prom_gauge_concurrency = Gauge(
60-
CONCURRENCY_NAME_PROMETHEUS, CONCURRENCY_DESCRIPTION, ["function", "module"]
66+
CONCURRENCY_NAME_PROMETHEUS,
67+
CONCURRENCY_DESCRIPTION,
68+
[
69+
"function",
70+
"module",
71+
SERVICE_NAME_PROMETHEUS,
72+
],
6173
)
6274

6375
def __init__(self) -> None:
@@ -81,10 +93,12 @@ def _count(
8193
if objective is None or objective.success_rate is None
8294
else objective.success_rate.value
8395
)
96+
service_name = get_service_name()
8497

8598
self.prom_counter.labels(
8699
func_name,
87100
module_name,
101+
service_name,
88102
result.value,
89103
caller_module,
90104
caller_function,
@@ -110,10 +124,12 @@ def _histogram(
110124
if latency is not None:
111125
threshold = latency[0].value
112126
percentile = latency[1].value
127+
service_name = get_service_name()
113128

114129
self.prom_histogram.labels(
115130
func_name,
116131
module_name,
132+
service_name,
117133
objective_name,
118134
percentile,
119135
threshold,
@@ -122,14 +138,18 @@ def _histogram(
122138
def set_build_info(self, commit: str, version: str, branch: str):
123139
if not self._has_set_build_info:
124140
self._has_set_build_info = True
125-
self.prom_gauge_build_info.labels(commit, version, branch).set(1)
141+
service_name = get_service_name()
142+
self.prom_gauge_build_info.labels(
143+
commit, version, branch, service_name
144+
).set(1)
126145

127146
def start(
128147
self, function: str, module: str, track_concurrency: Optional[bool] = False
129148
):
130149
"""Start tracking metrics for a function call."""
131150
if track_concurrency:
132-
self.prom_gauge_concurrency.labels(function, module).inc()
151+
service_name = get_service_name()
152+
self.prom_gauge_concurrency.labels(function, module, service_name).inc()
133153

134154
def finish(
135155
self,
@@ -159,7 +179,8 @@ def finish(
159179
self._histogram(function, module, start_time, objective, exemplar)
160180

161181
if track_concurrency:
162-
self.prom_gauge_concurrency.labels(function, module).dec()
182+
service_name = get_service_name()
183+
self.prom_gauge_concurrency.labels(function, module, service_name).dec()
163184

164185
def initialize_counters(
165186
self,

Diff for: src/autometrics/tracker/test_concurrency.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ async def test_concurrency_tracking_prometheus(monkeypatch):
3737
data = blob.decode("utf-8")
3838

3939
assert (
40-
f"""# TYPE function_calls_concurrent gauge\nfunction_calls_concurrent{{function="sleep",module="autometrics.tracker.test_concurrency"}} 1.0"""
40+
f"""# TYPE function_calls_concurrent gauge\nfunction_calls_concurrent{{function="sleep",module="autometrics.tracker.test_concurrency",service_name="autometrics"}} 1.0"""
4141
in data
4242
)
4343

Diff for: src/autometrics/tracker/test_tracker.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,9 @@ def test_init_prometheus_tracker_set_build_info(monkeypatch):
4444
blob = generate_latest()
4545
assert blob is not None
4646
data = blob.decode("utf-8")
47+
print(data)
4748

48-
prom_build_info = (
49-
f"""build_info{{branch="{branch}",commit="{commit}",version="{version}"}} 1.0"""
50-
)
49+
prom_build_info = f"""build_info{{branch="{branch}",commit="{commit}",service_name="autometrics",version="{version}"}} 1.0"""
5150
assert prom_build_info in data
5251

5352
monkeypatch.delenv("AUTOMETRICS_VERSION", raising=False)
@@ -79,9 +78,7 @@ def test_init_otel_tracker_set_build_info(monkeypatch):
7978
assert blob is not None
8079
data = blob.decode("utf-8")
8180

82-
prom_build_info = (
83-
f"""build_info{{branch="{branch}",commit="{commit}",version="{version}"}} 1.0"""
84-
)
81+
prom_build_info = f"""build_info{{branch="{branch}",commit="{commit}",service_name="autometrics",version="{version}",service_name="autometrics"}} 1.0"""
8582
assert prom_build_info in data
8683

8784
monkeypatch.delenv("AUTOMETRICS_VERSION", raising=False)

Diff for: src/autometrics/utils.py

+15
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,21 @@ def get_function_name(func: Callable) -> str:
2929
return func.__qualname__ or func.__name__
3030

3131

32+
service_name = None
33+
34+
35+
def get_service_name():
36+
global service_name
37+
if service_name is not None:
38+
return service_name
39+
service_name = (
40+
os.getenv("AUTOMETRICS_SERVICE_NAME")
41+
or os.getenv("OTEL_SERVICE_NAME")
42+
or __package__.rsplit(".", 1)[0]
43+
)
44+
return service_name
45+
46+
3247
def write_docs(func_name: str, module_name: str):
3348
"""Write the prometheus query urls to the function docstring."""
3449
generator = Generator(func_name, module_name)

0 commit comments

Comments
 (0)