-
Notifications
You must be signed in to change notification settings - Fork 8
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
Added Console Trace link writer #222
Changes from 3 commits
65addf5
1188dc0
aeaf146
8ebcf77
b99df48
a12d642
add5dbd
fc9b5e0
845509d
506fff4
54cbea1
b4c727e
9f6bc81
05a5768
4c36706
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,106 @@ | ||||
using OpenTelemetry; | ||||
using System; | ||||
using System.Diagnostics; | ||||
using System.Net.Http; | ||||
using System.Text.Json; | ||||
using System.Text.Json.Serialization; | ||||
|
||||
namespace Honeycomb.OpenTelemetry | ||||
{ | ||||
/// <summary> | ||||
/// Writes links to the Honeycomb UI for all root spans to the console | ||||
/// </summary> | ||||
public class ConsoleLinkExporter : BaseExporter<Activity> | ||||
{ | ||||
private string _apiKey; | ||||
private string _authApiHost; | ||||
private string _serviceName; | ||||
private string _teamSlug; | ||||
private string _environmentSlug; | ||||
|
||||
private bool IsEnabled = false; | ||||
|
||||
/// <summary> | ||||
/// Initializes the <see cref="ConsoleLinkExporter" /> class | ||||
/// </summary> | ||||
/// <param name="options">Settings for Link generation</param> | ||||
public ConsoleLinkExporter(HoneycombOptions options) | ||||
{ | ||||
_apiKey = options.ApiKey; | ||||
_authApiHost = options.TracesEndpoint; | ||||
_serviceName = options.ServiceName; | ||||
InitTraceLinkParameters(); | ||||
} | ||||
|
||||
private void InitTraceLinkParameters() | ||||
{ | ||||
if (string.IsNullOrEmpty(_apiKey)) | ||||
return; | ||||
|
||||
var httpClient = new HttpClient(); | ||||
httpClient.BaseAddress = new Uri(_authApiHost); | ||||
httpClient.DefaultRequestHeaders.Add("X-Honeycomb-Team", _apiKey); | ||||
|
||||
var response = httpClient.GetAsync("/1/auth").GetAwaiter().GetResult(); | ||||
if (!response.IsSuccessStatusCode) { | ||||
Console.WriteLine("Didn't get a valid response from Honeycomb"); | ||||
return; | ||||
} | ||||
|
||||
var responseString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); | ||||
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseString); | ||||
_environmentSlug = authResponse.Environment.Slug; | ||||
_teamSlug = authResponse.Team.Slug; | ||||
if (string.IsNullOrEmpty(_environmentSlug) || | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can generate trace links for classic datasets, it just needs to omit the environment segments. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would we detect if it's a classic dataset? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Classic (nee Legacy) API key is 32 characters:
|
||||
string.IsNullOrEmpty(_teamSlug)) { | ||||
Console.WriteLine("Team or Environment wasn't returned"); | ||||
return; | ||||
} | ||||
IsEnabled = true; | ||||
} | ||||
|
||||
/// <inheritdoc /> | ||||
public override ExportResult Export(in Batch<Activity> batch) | ||||
{ | ||||
if (!IsEnabled) | ||||
return ExportResult.Success; | ||||
|
||||
foreach (var activity in batch) | ||||
{ | ||||
if (string.IsNullOrEmpty(activity.ParentId)) | ||||
{ | ||||
Console.WriteLine($"Trace Emitted for {activity.DisplayName}"); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be nice to expand this in the future to write to an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually don't want it going through the logging framework, as that could take it into other pipelines, and this is specifically for local development. |
||||
Console.WriteLine($"TraceLink: {GetTraceLink(activity.TraceId.ToString())}"); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be nice to combine these into a single line for easier searching and reduce screen congestion for higher volume apps. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep in mind that this is just for the root span, not all spans, so even in large volume applications, we should only see a small amount of spans. 1 line is actually pretty long as the url is already quite long, which is why I extended it to 2. Happy to hear thoughts though, I'm not 100% convinced this is right. |
||||
} | ||||
} | ||||
return ExportResult.Success; | ||||
} | ||||
|
||||
private string GetTraceLink(string traceId) | ||||
{ | ||||
return $"http://ui.honeycomb.io/{_teamSlug}/environments/{_environmentSlug}/datasets/{_serviceName}/trace?trace_id={traceId}"; | ||||
} | ||||
} | ||||
|
||||
#pragma warning disable 1591 | ||||
public class AuthResponse | ||||
{ | ||||
[JsonPropertyName("environment")] | ||||
public HoneycombEnvironment Environment { get; set; } | ||||
|
||||
[JsonPropertyName("team")] | ||||
public Team Team { get; set; } | ||||
} | ||||
public class HoneycombEnvironment | ||||
{ | ||||
[JsonPropertyName("slug")] | ||||
public string Slug { get; set; } | ||||
} | ||||
|
||||
public class Team | ||||
{ | ||||
[JsonPropertyName("slug")] | ||||
public string Slug { get; set; } | ||||
} | ||||
|
||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ internal class EnvironmentOptions | |
private const string SampleRateKey = "HONEYCOMB_SAMPLE_RATE"; | ||
private const string ServiceNameKey = "SERVICE_NAME"; | ||
private const string ServiceVersionKey = "SERVICE_VERSION"; | ||
private const string WriteTraceLinksToConsoleKey = "WRITE_TRACE_LINKS_TO_CONSOLE"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels long, maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. using Console is more universal I think, and also keeps it specific to enviroments where you have access to the console. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in zoom convo, we thought |
||
private const uint DefaultSampleRate = 1; | ||
private const string DefaultApiEndpoint = "https://api.honeycomb.io:443"; | ||
private readonly IDictionary _environmentService; | ||
|
@@ -36,6 +37,7 @@ internal EnvironmentOptions(IDictionary service) | |
internal string MetricsEndpoint => GetEnvironmentVariable(MetricsEndpointKey, ApiEndpoint); | ||
internal string ServiceName => GetEnvironmentVariable(ServiceNameKey); | ||
internal string ServiceVersion => GetEnvironmentVariable(ServiceVersionKey); | ||
internal bool WriteTraceLinksToConsole => bool.TryParse(GetEnvironmentVariable(WriteTraceLinksToConsoleKey), out var writeTraceLinksToConsole) ? writeTraceLinksToConsole : false; | ||
internal uint SampleRate => uint.TryParse(GetEnvironmentVariable(SampleRateKey), out var sampleRate) ? sampleRate : DefaultSampleRate; | ||
|
||
private string GetEnvironmentVariable(string key, string defaultValue = "") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ public class HoneycombOptions | |
private string _tracesDataset; | ||
private string _tracesEndpoint; | ||
private string _metricsEndpoint; | ||
private bool _writeTraceLinksToConsole; | ||
|
||
/// <summary> | ||
/// Name of the Honeycomb section of IConfiguration | ||
|
@@ -68,6 +69,15 @@ internal bool IsMetricsLegacyKey() | |
return MetricsApiKey?.Length == 32; | ||
} | ||
|
||
/// <summary> | ||
/// Write links to honeycomb traces as they come in | ||
/// </summary> | ||
public bool WriteTraceLinksToConsole | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should match env var above. |
||
{ | ||
get { return _writeTraceLinksToConsole; } | ||
set { _writeTraceLinksToConsole = value; } | ||
} | ||
|
||
/// <summary> | ||
/// API key used to send trace telemetry data to Honeycomb. Defaults to <see cref="ApiKey"/>. | ||
/// </summary> | ||
|
@@ -193,7 +203,8 @@ public string MetricsEndpoint | |
/// (Optional) Options delegate to configure StackExchange.Redis instrumentation. | ||
/// </summary> | ||
public Action<StackExchangeRedisCallsInstrumentationOptions> | ||
ConfigureStackExchangeRedisClientInstrumentationOptions { get; set; } | ||
ConfigureStackExchangeRedisClientInstrumentationOptions | ||
{ get; set; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was this intentional, or did your IDE move this? |
||
|
||
/// <summary> | ||
/// (Optional) Additional <see cref="Meter"/> names for generating metrics. | ||
|
@@ -220,6 +231,7 @@ public Action<StackExchangeRedisCallsInstrumentationOptions> | |
{ "--honeycomb-traces-endpoint", "tracesendpoint" }, | ||
{ "--honeycomb-metrics-endpoint", "metricsendpoint" }, | ||
{ "--honeycomb-samplerate", "samplerate" }, | ||
{ "--honeycomb-write-trace-links-to-console", "writetracelinkstoconsole" }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should match env var |
||
{ "--service-name", "servicename" }, | ||
{ "--service-version", "serviceversion" }, | ||
{ "--instrument-http", "instrumenthttpclient" }, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think getting the team and environment slugs could be reusable so may end up being passed in instead of being resolved inside this span processor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that we should be calling out via http as part of startup in any other environment than production. It's an async call in a sync method, and I don't like it all. As it stands, because it's in the ConsoleLinkExporter, it won't be triggered unless you ask for tracelinks.
I think that's probably an internal decision too, so we can refactor that as and when we need it.