Skip to content

Commit

Permalink
Add error reporting tag and service context information to structured…
Browse files Browse the repository at this point in the history
… logging
  • Loading branch information
danielwinkler committed Feb 12, 2025
1 parent 8796a11 commit e2ca971
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,12 @@ public sealed class GoogleCloudConsoleFormatter : ConsoleFormatter, IDisposable

private static readonly JsonEncodedText s_messagePropertyName = JsonEncodedText.Encode("message");
private static readonly JsonEncodedText s_exceptionPropertyName = JsonEncodedText.Encode("exception");
private static readonly JsonEncodedText s_stackTracePropertyName = JsonEncodedText.Encode("stack_trace");
private static readonly JsonEncodedText s_severityPropertyName = JsonEncodedText.Encode("severity");
private static readonly JsonEncodedText s_categoryPropertyName = JsonEncodedText.Encode("category");
private static readonly JsonEncodedText s_scopesPropertyName = JsonEncodedText.Encode("scopes");
private static readonly JsonEncodedText s_jsonPayloadTypePropertyName = JsonEncodedText.Encode("@type");
private static readonly JsonEncodedText s_jsonPayloadTypePropertyValue = JsonEncodedText.Encode("type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent");
private static readonly JsonEncodedText s_formatParametersPropertyName = JsonEncodedText.Encode("format_parameters");

// `trace`, `spanId` and `trace_sampled` are special JSON log fields used for log trace correlation.
Expand Down Expand Up @@ -102,24 +105,42 @@ public override void Write<TState>(in LogEntry<TState> logEntry, IExternalScopeP
writer.WriteString(s_messagePropertyName, message);
writer.WriteString(s_categoryPropertyName, logEntry.Category);
writer.WriteString(s_severityPropertyName, GetSeverity(logEntry.LogLevel));
if (logEntry.Exception is object)
if (logEntry.Exception is not null)
{
writer.WriteString(s_exceptionPropertyName, ToInvariantString(logEntry.Exception));
writer.WriteString(s_exceptionPropertyName, ToInvariantString(logEntry.Exception.Message));
writer.WriteString(s_stackTracePropertyName, ToInvariantString(logEntry.Exception));
}

MaybeWriteServiceContext(writer);
MaybeWriteFormatParameters(writer, logEntry.State);
MaybeWriteScopeInformation(writer, scopeProvider);
MaybeWriteTraceInformation(writer);
MaybeWriteErrorReportingTag(writer, logEntry.LogLevel);
writer.WriteEndObject();
writer.Flush();
}
// TODO: It would be lovely to have a more efficient way of writing the JSON to the console
// than writing bytes and converting those bytes back into a string - but for the moment,
// this is what we've got.
textWriter.WriteLine(Encoding.UTF8.GetString(output.GetBuffer(), 0, (int) output.Position));
textWriter.WriteLine(Encoding.UTF8.GetString(output.GetBuffer(), 0, (int)output.Position));
}
}

private void MaybeWriteServiceContext(Utf8JsonWriter writer)
{
if (_options.ServiceIdentifier == null && _options.ServiceVersion == null)
{
return;
}
// in case of an error, we need to let GCP logging know and provide information about the context
// <see href=https://cloud.google.com/error-reporting/reference/rest/v1beta1/projects.events/report#ReportedErrorEvent"/>
writer.WriteStartObject("serviceContext");
if (_options.ServiceIdentifier != null) { writer.WriteString("service", _options.ServiceIdentifier); }
if (_options.ServiceVersion != null) { writer.WriteString("version", _options.ServiceVersion); }
writer.WriteEndObject();
}


private void MaybeWriteFormatParameters<TState>(Utf8JsonWriter writer, TState state)
{
// If we have format params and its more than just the original message add them.
Expand Down Expand Up @@ -209,6 +230,14 @@ private void MaybeWriteTraceInformation(Utf8JsonWriter writer)
writer.WriteBoolean(s_traceSampledPropertyName, activity.Recorded);
}

private void MaybeWriteErrorReportingTag(Utf8JsonWriter writer, LogLevel logLevel)
{
if (logLevel >= _options.ErrorReportingLogLevel)
{
writer.WriteString(s_jsonPayloadTypePropertyName, s_jsonPayloadTypePropertyValue);
}
}

private static JsonEncodedText GetSeverity(LogLevel logLevel) =>
logLevel switch
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;

namespace Google.Cloud.Logging.Console;
Expand Down Expand Up @@ -40,4 +41,24 @@ public class GoogleCloudConsoleFormatterOptions : ConsoleFormatterOptions
/// Note that when running your code in Google Cloud, for instance in Google Cloud Run, trace information is automatically collected and exported by the runtime.
/// </remarks>
public string TraceGoogleCloudProjectId { get; set; }


/// <summary>
/// The service identifier that will be used to annotate the logs. Recommended.
/// Should be set to the service name plus optionally the environment name.
/// <see href="https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext"/>
/// </summary>
public string ServiceIdentifier { get; set; }

/// <summary>
/// The version of the service that is being logged. Optional.
/// <see href="https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext"/>
/// </summary>
public string ServiceVersion { get; set; }

/// <summary>
/// The minimum severity level that should trigger Google Cloud Error Reporting.
/// <see href="https://cloud.google.com/error-reporting/docs/formatting-error-messages#log-text"/>
/// </summary>
public LogLevel ErrorReportingLogLevel { get; set; } = LogLevel.Error;
}

0 comments on commit e2ca971

Please sign in to comment.