Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions pkgs/google_cloud/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
## 0.4.0

### BREAKING CHANGES

- Renamed `RequestLogger` to `CloudLogger` and moved it to
`package:google_cloud/general.dart`.

### New Features

- `CloudLogger` is no longer abstract and has a default implementation that
prints to stdout.
- Added `payload`, `labels`, and `stackTrace` named parameters to
`CloudLogger` functions as well as `structuredLogEntry`.
- Hardened structured log JSON serialization with automatic fallback mechanisms
to safely handle native `toJson()` implementations and circular references
without failing.

## 0.3.1

- Fix a bug where `projectIdFromGcloudConfig()` used the incorrect gcloud shell
Expand Down
8 changes: 5 additions & 3 deletions pkgs/google_cloud/lib/general.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
/// {@canonicalFor gcp_project.MetadataServerException}
/// {@canonicalFor gcp_project.projectIdFromMetadataServer}
/// {@canonicalFor gcp_project.serviceAccountEmailFromMetadataServer}
/// {@canonicalFor logging.LogSeverity}
/// {@canonicalFor logging.structuredLogEntry}
/// {@canonicalFor logger.LogSeverity}
/// {@canonicalFor logger.CloudLogger}
/// {@canonicalFor structured_logging.structuredLogEntry}
/// {@canonicalFor metadata.gceMetadataHost}
/// {@canonicalFor metadata.gceMetadataUrl}
library;
Expand All @@ -38,5 +39,6 @@ export 'src/gcp_project.dart'
projectIdFromGcloudConfig,
projectIdFromMetadataServer,
serviceAccountEmailFromMetadataServer;
export 'src/logging.dart' show LogSeverity, structuredLogEntry;
export 'src/logger.dart' show CloudLogger, LogSeverity;
export 'src/metadata.dart' show gceMetadataHost, gceMetadataUrl;
export 'src/structured_logging.dart' show structuredLogEntry;
4 changes: 1 addition & 3 deletions pkgs/google_cloud/lib/http_serving.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
///
/// {@canonicalFor bad_configuration_exception.BadConfigurationException}
/// {@canonicalFor bad_request_exception.BadRequestException}
/// {@canonicalFor http_logging.RequestLogger}
/// {@canonicalFor http_logging.badRequestMiddleware}
/// {@canonicalFor http_logging.cloudLoggingMiddleware}
/// {@canonicalFor http_logging.createLoggingMiddleware}
Expand All @@ -50,13 +49,12 @@
/// {@canonicalFor terminate.waitForTerminate}
library;

export 'src/logger.dart' show CloudLogger, LogSeverity;
export 'src/serving/bad_configuration_exception.dart'
show BadConfigurationException;
export 'src/serving/bad_request_exception.dart' show BadRequestException;
export 'src/serving/http_logging.dart'
show
LogSeverity,
RequestLogger,
badRequestMiddleware,
cloudLoggingMiddleware,
createLoggingMiddleware,
Expand Down
221 changes: 221 additions & 0 deletions pkgs/google_cloud/lib/src/logger.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import 'package:meta/meta.dart';
import 'package:stack_trace/stack_trace.dart';

/// See https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity
enum LogSeverity implements Comparable<LogSeverity> {
defaultSeverity._(0, 'DEFAULT'),
debug._(100, 'DEBUG'),
info._(200, 'INFO'),
notice._(300, 'NOTICE'),
warning._(400, 'WARNING'),
error._(500, 'ERROR'),
critical._(600, 'CRITICAL'),
alert._(700, 'ALERT'),
emergency._(800, 'EMERGENCY');

final int value;
final String name;

const LogSeverity._(this.value, this.name);

@override
int compareTo(LogSeverity other) => value.compareTo(other.value);

bool operator <(LogSeverity other) => value < other.value;

bool operator <=(LogSeverity other) => value <= other.value;

bool operator >(LogSeverity other) => value > other.value;

bool operator >=(LogSeverity other) => value >= other.value;

@override
String toString() => 'LogSeverity $name ($value)';

String toJson() => name;
}

/// Allows logging at a specified severity.
///
/// Compatible with the
/// [log severities](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity)
/// supported by Google Cloud.
abstract base class CloudLogger {
/// Const constructor for subclasses.
const CloudLogger();

const factory CloudLogger.defaultLogger() = _DefaultLogger;

/// Logs [message] at the given [severity].
void log(
Object message,
LogSeverity severity, {
Map<String, Object?>? payload,
Map<String, String>? labels,
StackTrace? stackTrace,
});

/// Logs [message] at [LogSeverity.debug] severity.
void debug(
Object message, {
Map<String, Object?>? payload,
Map<String, String>? labels,
StackTrace? stackTrace,
}) => log(
message,
LogSeverity.debug,
payload: payload,
labels: labels,
stackTrace: stackTrace,
);

/// Logs [message] at [LogSeverity.info] severity.
void info(
Object message, {
Map<String, Object?>? payload,
Map<String, String>? labels,
StackTrace? stackTrace,
}) => log(
message,
LogSeverity.info,
payload: payload,
labels: labels,
stackTrace: stackTrace,
);

/// Logs [message] at [LogSeverity.notice] severity.
void notice(
Object message, {
Map<String, Object?>? payload,
Map<String, String>? labels,
StackTrace? stackTrace,
}) => log(
message,
LogSeverity.notice,
payload: payload,
labels: labels,
stackTrace: stackTrace,
);

/// Logs [message] at [LogSeverity.warning] severity.
void warning(
Object message, {
Map<String, Object?>? payload,
Map<String, String>? labels,
StackTrace? stackTrace,
}) => log(
message,
LogSeverity.warning,
payload: payload,
labels: labels,
stackTrace: stackTrace,
);

/// Logs [message] at [LogSeverity.error] severity.
void error(
Object message, {
Map<String, Object?>? payload,
Map<String, String>? labels,
StackTrace? stackTrace,
}) => log(
message,
LogSeverity.error,
payload: payload,
labels: labels,
stackTrace: stackTrace,
);

/// Logs [message] at [LogSeverity.critical] severity.
void critical(
Object message, {
Map<String, Object?>? payload,
Map<String, String>? labels,
StackTrace? stackTrace,
}) => log(
message,
LogSeverity.critical,
payload: payload,
labels: labels,
stackTrace: stackTrace,
);

/// Logs [message] at [LogSeverity.alert] severity.
void alert(
Object message, {
Map<String, Object?>? payload,
Map<String, String>? labels,
StackTrace? stackTrace,
}) => log(
message,
LogSeverity.alert,
payload: payload,
labels: labels,
stackTrace: stackTrace,
);

/// Logs [message] at [LogSeverity.emergency] severity.
void emergency(
Object message, {
Map<String, Object?>? payload,
Map<String, String>? labels,
StackTrace? stackTrace,
}) => log(
message,
LogSeverity.emergency,
payload: payload,
labels: labels,
stackTrace: stackTrace,
);
}

/// A [CloudLogger] that prints messages normally.
///
/// Any message that's not [LogSeverity.defaultSeverity] is prefixed by the
/// [LogSeverity] name.
final class _DefaultLogger extends CloudLogger {
/// Const constructor.
const _DefaultLogger();

@override
void log(
Object message,
LogSeverity severity, {
Map<String, Object?>? payload,
Map<String, String>? labels,
StackTrace? stackTrace,
}) {
final payloadStr = payload != null ? ' $payload' : '';
final labelsStr = labels != null && labels.isNotEmpty ? ' $labels' : '';
final traceStr = stackTrace != null
? '\n${formatStackTrace(stackTrace)}'
: '';
if (severity == LogSeverity.defaultSeverity) {
print('$message$payloadStr$labelsStr$traceStr');
} else {
print('${severity.name}: $message$payloadStr$labelsStr$traceStr');
}
}
}

@internal
bool frameFolder(Frame frame) =>
frame.isCore || frame.package == 'google_cloud';

@internal
Chain formatStackTrace(StackTrace? stackTrace) =>
(stackTrace == null ? Chain.current() : Chain.forTrace(stackTrace))
.foldFrames(frameFolder, terse: true);
Loading