Send traces from your product

Course

This procedure is a part of a course that teaches you how to build a quickstart. If you haven't already, checkout the course introduction.

Each procedure in this course builds on top of the last one, so make sure you've completed the last procedure, send logs from your product before proceeding with this one.

Traces capture details of a single request as it moves through a system. They’re composed of spans, which are data structures that represent individual operations in the flow of execution.

New Relic, provides you a variety of ways to instrument your application to send traces to our Trace API.

In this lesson, you learn to send traces from your product using our telemetry software development kit (SDK).

Use our SDK

We offer an open source telemetry SDK in several of the most popular programming languages such as Python, Java, Node/TypeScript. These send data to our data ingest APIs, including our Trace API.

In this lesson, you learn how to install and use the Python telemetry SDK to report your first span to New Relic.

Report your first span

Step 1 of 7

Change to the send-traces/flashDB direcrory of the course repository.

bash
$
cd ../../send-traces/flashDB
Step 2 of 7

If you haven't already, install the newrelic-telemetry-sdk package.

bash
$
pip install newrelic-telemetry-sdk
Step 3 of 7

Open db.py file in the IDE of your choice and configure the SpanClient.

1
import os
2
import random
3
import datetime
4
from sys import getsizeof
5
import psutil
6
7
from newrelic_telemetry_sdk import MetricClient, GaugeMetric, CountMetric, SummaryMetric
8
from newrelic_telemetry_sdk import EventClient, Event
9
from newrelic_telemetry_sdk import LogClient, Log
10
from newrelic_telemetry_sdk import SpanClient
11
12
metric_client = MetricClient(os.environ["NEW_RELIC_LICENSE_KEY"])
13
event_client = EventClient(os.environ["NEW_RELIC_LICENSE_KEY"])
14
log_client = LogClient(os.environ["NEW_RELIC_LICENSE_KEY"])
15
span_client = SpanClient(os.environ["NEW_RELIC_LICENSE_KEY"])
16
17
db = {}
18
stats = {
19
"read_response_times": [],
20
"read_errors": 0,
21
"read_count": 0,
22
"create_response_times": [],
23
"create_errors": 0,
24
"create_count": 0,
25
"update_response_times": [],
26
"update_errors": 0,
27
"update_count": 0,
28
"delete_response_times": [],
29
"delete_errors": 0,
30
"delete_count": 0,
31
"cache_hit": 0,
32
}
33
last_push = {
34
"read": datetime.datetime.now(),
35
"create": datetime.datetime.now(),
36
"update": datetime.datetime.now(),
37
"delete": datetime.datetime.now(),
38
}
39
40
def read(key):
41
42
print(f"Reading...")
43
44
if random.randint(0, 30) > 10:
45
stats["cache_hit"] += 1
46
47
stats["read_response_times"].append(random.uniform(0.5, 1.0))
48
if random.choice([True, False]):
49
stats["read_errors"] += 1
50
stats["read_count"] += 1
51
try_send("read")
52
53
def create(key, value):
54
55
print(f"Writing...")
56
57
db[key] = value
58
stats["create_response_times"].append(random.uniform(0.5, 1.0))
59
if random.choice([True, False]):
60
stats["create_errors"] += 1
61
stats["create_count"] += 1
62
try_send("create")
63
64
def update(key, value):
65
66
print(f"Updating...")
67
68
db[key] = value
69
stats["update_response_times"].append(random.uniform(0.5, 1.0))
70
if random.choice([True, False]):
71
stats["update_errors"] += 1
72
stats["update_count"] += 1
73
try_send("update")
74
75
def delete(key):
76
77
print(f"Deleting...")
78
79
db.pop(key, None)
80
stats["delete_response_times"].append(random.uniform(0.5, 1.0))
81
if random.choice([True, False]):
82
stats["delete_errors"] += 1
83
stats["delete_count"] += 1
84
try_send("delete")
85
86
def try_send(type_):
87
88
print("try_send")
89
90
now = datetime.datetime.now()
91
interval_ms = (now - last_push[type_]).total_seconds() * 1000
92
if interval_ms >= 2000:
93
send_metrics(type_, interval_ms)
94
send_event(type_)
95
send_logs()
96
97
def send_metrics(type_, interval_ms):
98
99
print("sending metrics...")
100
101
keys = GaugeMetric("fdb_keys", len(db))
102
db_size = GaugeMetric("fdb_size", getsizeof(db))
103
104
errors = CountMetric(
105
name=f"fdb_{type_}_errors",
106
value=stats[f"{type_}_errors"],
107
interval_ms=interval_ms
108
)
109
110
cache_hits = CountMetric(
111
name=f"fdb_cache_hits",
112
value=stats["cache_hit"],
113
interval_ms=interval_ms
114
)
115
116
response_times = stats[f"{type_}_response_times"]
117
response_time_summary = SummaryMetric(
118
f"fdb_{type_}_responses",
119
count=len(response_times),
120
min=min(response_times),
121
max=max(response_times),
122
sum=sum(response_times),
123
interval_ms=interval_ms,
124
)
125
126
batch = [keys, db_size, errors, cache_hits, response_time_summary]
127
response = metric_client.send_batch(batch)
128
response.raise_for_status()
129
print("Sent metrics successfully!")
130
clear(type_)
131
132
def send_event(type_):
133
134
print("sending event...")
135
136
count = Event(
137
"fdb_method", {"method": type_}
138
)
139
140
response = event_client.send_batch(count)
141
response.raise_for_status()
142
print("Event sent successfully!")
143
144
def send_logs():
145
146
print("sending log...")
147
148
process = psutil.Process(os.getpid())
149
memory_usage = process.memory_percent()
150
151
log = Log("FlashDB is using " + str(round(memory_usage * 100, 2)) + "% memory")
152
153
response = log_client.send(log)
154
response.raise_for_status()
155
print("Log sent successfully!")
156
157
def clear(type_):
158
stats[f"{type_}_response_times"] = []
159
stats[f"{type_}_errors"] = 0
160
stats["cache_hit"] = 0
161
stats[f"{type_}_count"] = 0
162
last_push[type_] = datetime.datetime.now()
db.py

Important

This example expects an environment variable called $NEW_RELIC_LICENSE_KEY.

Step 4 of 7

Instrument your app to report a span to New Relic.

1
import os
2
import random
3
import datetime
4
from sys import getsizeof
5
import psutil
6
import time
7
8
from newrelic_telemetry_sdk import MetricClient, GaugeMetric, CountMetric, SummaryMetric
9
from newrelic_telemetry_sdk import EventClient, Event
10
from newrelic_telemetry_sdk import LogClient, Log
11
from newrelic_telemetry_sdk import SpanClient, Span
12
13
metric_client = MetricClient(os.environ["NEW_RELIC_LICENSE_KEY"])
14
event_client = EventClient(os.environ["NEW_RELIC_LICENSE_KEY"])
15
log_client = LogClient(os.environ["NEW_RELIC_LICENSE_KEY"])
16
span_client = SpanClient(os.environ["NEW_RELIC_LICENSE_KEY"])
17
18
db = {}
19
stats = {
20
"read_response_times": [],
21
"read_errors": 0,
22
"read_count": 0,
23
"create_response_times": [],
24
"create_errors": 0,
25
"create_count": 0,
26
"update_response_times": [],
27
"update_errors": 0,
28
"update_count": 0,
29
"delete_response_times": [],
30
"delete_errors": 0,
31
"delete_count": 0,
32
"cache_hit": 0,
33
}
34
last_push = {
35
"read": datetime.datetime.now(),
36
"create": datetime.datetime.now(),
37
"update": datetime.datetime.now(),
38
"delete": datetime.datetime.now(),
39
}
40
41
def read(key):
42
43
print(f"Reading...")
44
45
if random.randint(0, 30) > 10:
46
stats["cache_hit"] += 1
47
48
stats["read_response_times"].append(random.uniform(0.5, 1.0))
49
if random.choice([True, False]):
50
stats["read_errors"] += 1
51
stats["read_count"] += 1
52
try_send("read")
53
54
def create(key, value):
55
56
print(f"Writing...")
57
58
db[key] = value
59
stats["create_response_times"].append(random.uniform(0.5, 1.0))
60
if random.choice([True, False]):
61
stats["create_errors"] += 1
62
stats["create_count"] += 1
63
try_send("create")
64
65
def update(key, value):
66
67
print(f"Updating...")
68
69
db[key] = value
70
stats["update_response_times"].append(random.uniform(0.5, 1.0))
71
if random.choice([True, False]):
72
stats["update_errors"] += 1
73
stats["update_count"] += 1
74
try_send("update")
75
76
def delete(key):
77
78
print(f"Deleting...")
79
80
db.pop(key, None)
81
stats["delete_response_times"].append(random.uniform(0.5, 1.0))
82
if random.choice([True, False]):
83
stats["delete_errors"] += 1
84
stats["delete_count"] += 1
85
try_send("delete")
86
87
def try_send(type_):
88
89
print("try_send")
90
91
now = datetime.datetime.now()
92
interval_ms = (now - last_push[type_]).total_seconds() * 1000
93
if interval_ms >= 2000:
94
send_metrics(type_, interval_ms)
95
send_event(type_)
96
send_logs()
97
98
def send_metrics(type_, interval_ms):
99
100
print("sending metrics...")
101
102
keys = GaugeMetric("fdb_keys", len(db))
103
db_size = GaugeMetric("fdb_size", getsizeof(db))
104
105
errors = CountMetric(
106
name=f"fdb_{type_}_errors",
107
value=stats[f"{type_}_errors"],
108
interval_ms=interval_ms
109
)
110
111
cache_hits = CountMetric(
112
name=f"fdb_cache_hits",
113
value=stats["cache_hit"],
114
interval_ms=interval_ms
115
)
116
117
response_times = stats[f"{type_}_response_times"]
118
response_time_summary = SummaryMetric(
119
f"fdb_{type_}_responses",
120
count=len(response_times),
121
min=min(response_times),
122
max=max(response_times),
123
sum=sum(response_times),
124
interval_ms=interval_ms,
125
)
126
127
batch = [keys, db_size, errors, cache_hits, response_time_summary]
128
response = metric_client.send_batch(batch)
129
response.raise_for_status()
130
print("Sent metrics successfully!")
131
clear(type_)
132
133
def send_event(type_):
134
135
print("sending event...")
136
137
count = Event(
138
"fdb_method", {"method": type_}
139
)
140
141
response = event_client.send_batch(count)
142
response.raise_for_status()
143
print("Event sent successfully!")
144
145
def send_logs():
146
147
print("sending log...")
148
149
process = psutil.Process(os.getpid())
150
memory_usage = process.memory_percent()
151
152
log = Log("FlashDB is using " + str(round(memory_usage * 100, 2)) + "% memory")
153
154
response = log_client.send(log)
155
response.raise_for_status()
156
print("Log sent successfully!")
157
158
def send_spans():
159
160
print("sending span...")
161
162
with Span(name="sleep") as span:
163
time.sleep(0.5)
164
165
response = span_client.send(span)
166
response.raise_for_status()
167
print("Span sleep sent successfully!")
168
169
def clear(type_):
170
stats[f"{type_}_response_times"] = []
171
stats[f"{type_}_errors"] = 0
172
stats["cache_hit"] = 0
173
stats[f"{type_}_count"] = 0
174
last_push[type_] = datetime.datetime.now()
db.py

Here, you instrument your platform to send a simple sleep span to New Relic.

Step 5 of 7

Amend the try_send module to send the span every 2 second.

1
import os
2
import random
3
import datetime
4
from sys import getsizeof
5
import psutil
6
import time
7
8
from newrelic_telemetry_sdk import MetricClient, GaugeMetric, CountMetric, SummaryMetric
9
from newrelic_telemetry_sdk import EventClient, Event
10
from newrelic_telemetry_sdk import LogClient, Log
11
from newrelic_telemetry_sdk import SpanClient, Span
12
13
metric_client = MetricClient(os.environ["NEW_RELIC_LICENSE_KEY"])
14
event_client = EventClient(os.environ["NEW_RELIC_LICENSE_KEY"])
15
log_client = LogClient(os.environ["NEW_RELIC_LICENSE_KEY"])
16
span_client = SpanClient(os.environ["NEW_RELIC_LICENSE_KEY"])
17
18
db = {}
19
stats = {
20
"read_response_times": [],
21
"read_errors": 0,
22
"read_count": 0,
23
"create_response_times": [],
24
"create_errors": 0,
25
"create_count": 0,
26
"update_response_times": [],
27
"update_errors": 0,
28
"update_count": 0,
29
"delete_response_times": [],
30
"delete_errors": 0,
31
"delete_count": 0,
32
"cache_hit": 0,
33
}
34
last_push = {
35
"read": datetime.datetime.now(),
36
"create": datetime.datetime.now(),
37
"update": datetime.datetime.now(),
38
"delete": datetime.datetime.now(),
39
}
40
41
def read(key):
42
43
print(f"Reading...")
44
45
if random.randint(0, 30) > 10:
46
stats["cache_hit"] += 1
47
48
stats["read_response_times"].append(random.uniform(0.5, 1.0))
49
if random.choice([True, False]):
50
stats["read_errors"] += 1
51
stats["read_count"] += 1
52
try_send("read")
53
54
def create(key, value):
55
56
print(f"Writing...")
57
58
db[key] = value
59
stats["create_response_times"].append(random.uniform(0.5, 1.0))
60
if random.choice([True, False]):
61
stats["create_errors"] += 1
62
stats["create_count"] += 1
63
try_send("create")
64
65
def update(key, value):
66
67
print(f"Updating...")
68
69
db[key] = value
70
stats["update_response_times"].append(random.uniform(0.5, 1.0))
71
if random.choice([True, False]):
72
stats["update_errors"] += 1
73
stats["update_count"] += 1
74
try_send("update")
75
76
def delete(key):
77
78
print(f"Deleting...")
79
80
db.pop(key, None)
81
stats["delete_response_times"].append(random.uniform(0.5, 1.0))
82
if random.choice([True, False]):
83
stats["delete_errors"] += 1
84
stats["delete_count"] += 1
85
try_send("delete")
86
87
def try_send(type_):
88
89
print("try_send")
90
91
now = datetime.datetime.now()
92
interval_ms = (now - last_push[type_]).total_seconds() * 1000
93
if interval_ms >= 2000:
94
send_metrics(type_, interval_ms)
95
send_event(type_)
96
send_logs()
97
send_spans()
98
99
def send_metrics(type_, interval_ms):
100
101
print("sending metrics...")
102
103
keys = GaugeMetric("fdb_keys", len(db))
104
db_size = GaugeMetric("fdb_size", getsizeof(db))
105
106
errors = CountMetric(
107
name=f"fdb_{type_}_errors",
108
value=stats[f"{type_}_errors"],
109
interval_ms=interval_ms
110
)
111
112
cache_hits = CountMetric(
113
name=f"fdb_cache_hits",
114
value=stats["cache_hit"],
115
interval_ms=interval_ms
116
)
117
118
response_times = stats[f"{type_}_response_times"]
119
response_time_summary = SummaryMetric(
120
f"fdb_{type_}_responses",
121
count=len(response_times),
122
min=min(response_times),
123
max=max(response_times),
124
sum=sum(response_times),
125
interval_ms=interval_ms,
126
)
127
128
batch = [keys, db_size, errors, cache_hits, response_time_summary]
129
response = metric_client.send_batch(batch)
130
response.raise_for_status()
131
print("Sent metrics successfully!")
132
clear(type_)
133
134
def send_event(type_):
135
136
print("sending event...")
137
138
count = Event(
139
"fdb_method", {"method": type_}
140
)
141
142
response = event_client.send_batch(count)
143
response.raise_for_status()
144
print("Event sent successfully!")
145
146
def send_logs():
147
148
print("sending log...")
149
150
process = psutil.Process(os.getpid())
151
memory_usage = process.memory_percent()
152
153
log = Log("FlashDB is using " + str(round(memory_usage * 100, 2)) + "% memory")
154
155
response = log_client.send(log)
156
response.raise_for_status()
157
print("Log sent successfully!")
158
159
def send_spans():
160
161
print("sending span...")
162
163
with Span(name="sleep") as span:
164
time.sleep(0.5)
165
166
response = span_client.send(span)
167
response.raise_for_status()
168
print("Span sleep sent successfully!")
169
170
def clear(type_):
171
stats[f"{type_}_response_times"] = []
172
stats[f"{type_}_errors"] = 0
173
stats["cache_hit"] = 0
174
stats[f"{type_}_count"] = 0
175
last_push[type_] = datetime.datetime.now()
db.py

Your platform will now report this span every 2 seconds.

Step 6 of 7

Navigate to the root of your application at build-a-quickstart-lab/send-traces/flashDB.

Step 7 of 7

Run your services to verify that it is reporting the span.

bash
$
python simulator.py
Writing...
try_send
Reading...
try_send
Reading...
try_send
Writing...
try_send
Writing...
try_send
Reading...
sending metrics...
Sent metrics successfully!
sending event...
Event sent successfully!
sending log...
Log sent successfully!
sending span...
Span sleep sent successfully!

Alternative Options

If the language SDK doesn’t fit your needs, try out one of our other options:

  • Existing Zipkin instrumentation: if you have an existing Zipkin implementation, you can simply change the endpoint to New Relic to report your data. Read our documentation to report data from existing Zipkin instrumentation.
  • Manual Implementation: If the previous options don’t fit your requirements, you can always manually instrument your own library to make a POST request to the New Relic Trace API.

Your platform is now reporting data to New Relic. Next, you observe this data in New Relic using dashboard.

Course

This procedure is a part of course that teaches you how to build a quickstart. Continue to next lesson, create a dashboard.