diff --git a/build-system/s3-cache-scripts/earthly-s3-cache.sh b/build-system/s3-cache-scripts/earthly-s3-cache.sh index 571eb210450..01267639be0 100755 --- a/build-system/s3-cache-scripts/earthly-s3-cache.sh +++ b/build-system/s3-cache-scripts/earthly-s3-cache.sh @@ -19,7 +19,7 @@ function s3_upload() { if [ "${S3_BUILD_CACHE_UPLOAD:-true}" = "false" ] || [ "${AWS_ACCESS_KEY_ID}" == "" ] ; then return 0 # exit silently fi - /usr/src/build-system/s3-cache-scripts/cache-upload.sh "$FILE" $build_artifacts + /usr/src/build-system/s3-cache-scripts/cache-upload.sh "$FILE" $build_artifacts || echo "WARNING: S3 upload failed!" >&2 } function minio_download() { if [ -z "$S3_BUILD_CACHE_MINIO_URL" ] ; then @@ -35,7 +35,7 @@ function minio_upload() { fi # minio is S3-compatible S3_BUILD_CACHE_AWS_PARAMS="--endpoint-url $S3_BUILD_CACHE_MINIO_URL" AWS_SECRET_ACCESS_KEY=minioadmin AWS_ACCESS_KEY_ID=minioadmin \ - /usr/src/build-system/s3-cache-scripts/cache-upload.sh "$FILE" $build_artifacts + /usr/src/build-system/s3-cache-scripts/cache-upload.sh "$FILE" $build_artifacts || echo "WARNING Minio upload failed!" >&2 } # commands @@ -53,5 +53,5 @@ if ! bash -c "$command" ; then exit 1 # we have failed to build, don't continue fi -minio_upload || echo "Minio upload failed!" -s3_upload || echo "S3 upload failed!" +minio_upload +s3_upload diff --git a/scripts/run_native_testnet_with_metrics.sh b/scripts/run_native_testnet_with_metrics.sh new file mode 100755 index 00000000000..24cb581cace --- /dev/null +++ b/scripts/run_native_testnet_with_metrics.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -eu + +NAMESPACE=${1:-staging} + +echo "Trying to port forward. NOTE: Must be using a production k8s context with metrics chart." + +# Helper function to get load balancer URL based on namespace and service name +function get_load_balancer_url() { + local namespace=$1 + local service_name=$2 + kubectl get svc -n $namespace -o jsonpath="{.items[?(@.metadata.name=='$service_name')].status.loadBalancer.ingress[0].hostname}" +} + +# Fetch the service URLs based on the namespace for injection in the test-transfer.sh +OTEL_URL=http://$(get_load_balancer_url metrics metrics-opentelemetry-collector):4318 + +export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=$OTEL_URL/v1/metrics +export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=$OTEL_URL/v1/trace +export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=$OTEL_URL/v1/logs +export LOG_JSON=1 + +# re-enter script dir +cd $(dirname "${BASH_SOURCE[0]}") +./run_native_testnet.sh $@ \ No newline at end of file diff --git a/spartan/aztec-network/templates/_helpers.tpl b/spartan/aztec-network/templates/_helpers.tpl index 2fcc4b7b974..f9be2d9ecaa 100644 --- a/spartan/aztec-network/templates/_helpers.tpl +++ b/spartan/aztec-network/templates/_helpers.tpl @@ -102,6 +102,13 @@ http://{{ include "aztec-network.fullname" . }}-metrics.{{ .Release.Namespace }} {{- end -}} {{- end -}} +{{- define "aztec-network.otelCollectorLogsEndpoint" -}} +{{- if .Values.telemetry.enabled -}} +{{- if .Values.telemetry.otelCollectorEndpoint -}} +{{- .Values.telemetry.otelCollectorEndpoint -}}/v1/logs +{{- end -}} +{{- end -}} +{{- end -}} {{- define "helpers.flag" -}} {{- $name := index . 0 -}} diff --git a/spartan/aztec-network/templates/boot-node.yaml b/spartan/aztec-network/templates/boot-node.yaml index 3467935296b..59f99ba4a7f 100644 --- a/spartan/aztec-network/templates/boot-node.yaml +++ b/spartan/aztec-network/templates/boot-node.yaml @@ -141,6 +141,8 @@ spec: value: {{ include "aztec-network.otelCollectorMetricsEndpoint" . | quote }} - name: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT value: {{ include "aztec-network.otelCollectorTracesEndpoint" . | quote }} + - name: OTEL_EXPORTER_OTLP_LOGS_ENDPOINT + value: {{ include "aztec-network.otelCollectorLogsEndpoint" . | quote }} ports: - containerPort: {{ .Values.bootNode.service.nodePort }} - containerPort: {{ .Values.bootNode.service.p2pTcpPort }} diff --git a/spartan/aztec-network/templates/validator.yaml b/spartan/aztec-network/templates/validator.yaml index acd2eb9b937..963e778d745 100644 --- a/spartan/aztec-network/templates/validator.yaml +++ b/spartan/aztec-network/templates/validator.yaml @@ -130,6 +130,8 @@ spec: value: {{ include "aztec-network.otelCollectorMetricsEndpoint" . | quote }} - name: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT value: {{ include "aztec-network.otelCollectorTracesEndpoint" . | quote }} + - name: OTEL_EXPORTER_OTLP_LOGS_ENDPOINT + value: {{ include "aztec-network.otelCollectorLogsEndpoint" . | quote }} ports: - containerPort: {{ .Values.validator.service.nodePort }} - containerPort: {{ .Values.validator.service.p2pTcpPort }} diff --git a/spartan/metrics/install-prod.sh b/spartan/metrics/install-prod.sh index cd62570853d..849dbcd1cc9 100755 --- a/spartan/metrics/install-prod.sh +++ b/spartan/metrics/install-prod.sh @@ -1,4 +1,6 @@ #!/bin/bash set -eu -helm upgrade metrics . -n metrics --values "./values/prod.yaml" --install --create-namespace --atomic +cd "$(dirname "${BASH_SOURCE[0]}")" + +helm upgrade metrics . -n metrics --values "./values/prod.yaml" --install --create-namespace --atomic $@ diff --git a/spartan/metrics/install.sh b/spartan/metrics/install.sh index fda4203601f..2712e900756 100755 --- a/spartan/metrics/install.sh +++ b/spartan/metrics/install.sh @@ -1,6 +1,8 @@ #!/bin/bash set -eu +cd "$(dirname "${BASH_SOURCE[0]}")" + helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts helm repo add grafana https://grafana.github.io/helm-charts helm repo add prometheus-community https://prometheus-community.github.io/helm-charts diff --git a/spartan/metrics/values.yaml b/spartan/metrics/values.yaml index bc969d3c1a6..9e33411896c 100644 --- a/spartan/metrics/values.yaml +++ b/spartan/metrics/values.yaml @@ -1,5 +1,5 @@ opentelemetry-collector: - mode: daemonset + mode: deployment service: enabled: true @@ -28,9 +28,6 @@ opentelemetry-collector: protocol: TCP presets: - logsCollection: - enabled: true - includeCollectorLogs: true kubernetesAttributes: enabled: true config: diff --git a/spartan/scripts/deploy_spartan.sh b/spartan/scripts/deploy_spartan.sh index 85d7b12e1a8..ee78d504cf3 100755 --- a/spartan/scripts/deploy_spartan.sh +++ b/spartan/scripts/deploy_spartan.sh @@ -1,10 +1,15 @@ #!/bin/bash -set -eux +set -eu set -o pipefail TAG=$1 VALUES=$2 NAMESPACE=${3:-spartan} +PROD=${4:-true} +PROD_ARGS="" +if [ "$PROD" = "true" ] ; then + PROD_ARGS="--set network.public=true --set telemetry.enabled=true --set telemetry.otelCollectorEndpoint=http://metrics-opentelemetry-collector.metrics:4318" +fi SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" if [ -z "$TAG" ]; then @@ -46,16 +51,14 @@ function upgrade() { helm template $NAMESPACE $SCRIPT_DIR/../aztec-network \ --namespace $NAMESPACE \ --create-namespace \ - --values $SCRIPT_DIR/../aztec-network/values/$VALUES.yaml \ - --set images.aztec.image="$IMAGE" \ - --set network.public=true + --values $SCRIPT_DIR/../aztec-network/values/$VALUES.yaml $PROD_ARGS \ + --set images.aztec.image="$IMAGE" else helm upgrade --install $NAMESPACE $SCRIPT_DIR/../aztec-network \ --namespace $NAMESPACE \ --create-namespace \ - --values $SCRIPT_DIR/../aztec-network/values/$VALUES.yaml \ + --values $SCRIPT_DIR/../aztec-network/values/$VALUES.yaml $PROD_ARGS \ --set images.aztec.image="$IMAGE" \ - --set network.public=true \ --wait \ --wait-for-jobs=true \ --timeout=30m 2>&1 diff --git a/spartan/scripts/post_deploy_spartan.sh b/spartan/scripts/post_deploy_spartan.sh new file mode 100755 index 00000000000..bcf66bff49b --- /dev/null +++ b/spartan/scripts/post_deploy_spartan.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Targets a running cluster and deploys example contracts for testing +set -eu +set -o pipefail + +echo "Bootstrapping network with test contracts" + +NAMESPACE=${1:-spartan} +TAG=${2:-latest} +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +if [ -z "$NAMESPACE" ]; then + echo "Usage: $0 (optional: )" + echo "Example: $0 devnet" + exit 1 +fi + +# Helper function to get load balancer URL based on namespace and service name +function get_load_balancer_url() { + local namespace=$1 + local service_name=$2 + kubectl get svc -n $namespace -o jsonpath="{.items[?(@.metadata.name=='$service_name')].status.loadBalancer.ingress[0].hostname}" +} + +# Fetch the service URLs based on the namespace for injection in the test-transfer.sh +export BOOTNODE_URL=http://$(get_load_balancer_url $NAMESPACE "$NAMESPACE-aztec-network-boot-node-lb-tcp"):8080 +export PXE_URL=http://$(get_load_balancer_url $NAMESPACE "$NAMESPACE-aztec-network-pxe-lb"):8080 +export ETHEREUM_HOST=http://$(get_load_balancer_url $NAMESPACE "$NAMESPACE-aztec-network-ethereum-lb"):8545 + +echo "BOOTNODE_URL: $BOOTNODE_URL" +echo "PXE_URL: $PXE_URL" +echo "ETHEREUM_HOST: $ETHEREUM_HOST" + +echo "Bootstrapping contracts for test network. NOTE: This took one hour last run." +# hack to ensure L2 contracts are considered deployed +docker run aztecprotocol/aztec:$TAG bootstrap-network \ + --rpc-url $BOOTNODE_URL \ + --l1-rpc-url $ETHEREUM_HOST \ + --l1-chain-id 31337 \ + --l1-private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ + --json | tee ./basic_contracts.json diff --git a/spartan/scripts/test_spartan.sh b/spartan/scripts/test_spartan.sh index f6b3bbe2e9a..7154aeb01de 100755 --- a/spartan/scripts/test_spartan.sh +++ b/spartan/scripts/test_spartan.sh @@ -16,10 +16,17 @@ fi echo "Note: Repo should be bootstrapped with ./bootstrap.sh fast." +# Helper function to get load balancer URL based on namespace and service name +function get_load_balancer_url() { + local namespace=$1 + local service_name=$2 + kubectl get svc -n $namespace -o jsonpath="{.items[?(@.metadata.name=='$service_name')].status.loadBalancer.ingress[0].hostname}" +} + # Fetch the service URLs based on the namespace for injection in the test-transfer.sh -export BOOTNODE_URL=http://$(kubectl get svc -n $NAMESPACE -o jsonpath="{.items[?(@.metadata.name=='$NAMESPACE-aztec-network-boot-node-lb-tcp')].status.loadBalancer.ingress[0].hostname}"):8080 -export PXE_URL=http://$(kubectl get svc -n $NAMESPACE -o jsonpath="{.items[?(@.metadata.name=='$NAMESPACE-aztec-network-pxe-lb')].status.loadBalancer.ingress[0].hostname}"):8080 -export ETHEREUM_HOST=http://$(kubectl get svc -n $NAMESPACE -o jsonpath="{.items[?(@.metadata.name=='$NAMESPACE-aztec-network-ethereum-lb')].status.loadBalancer.ingress[0].hostname}"):8545 +export BOOTNODE_URL=http://$(get_load_balancer_url $NAMESPACE "$NAMESPACE-aztec-network-boot-node-lb-tcp"):8080 +export PXE_URL=http://$(get_load_balancer_url $NAMESPACE "$NAMESPACE-aztec-network-pxe-lb"):8080 +export ETHEREUM_HOST=http://$(get_load_balancer_url $NAMESPACE "$NAMESPACE-aztec-network-ethereum-lb"):8545 echo "BOOTNODE_URL: $BOOTNODE_URL" echo "PXE_URL: $PXE_URL" @@ -27,4 +34,4 @@ echo "ETHEREUM_HOST: $ETHEREUM_HOST" # hack to ensure L2 contracts are considered deployed touch $SCRIPT_DIR/../../yarn-project/end-to-end/scripts/native-network/state/l2-contracts.env -bash -x $SCRIPT_DIR/../../yarn-project/end-to-end/scripts/native-network/test-transfer.sh +bash -x $SCRIPT_DIR/../../yarn-project/end-to-end/scripts/native-network/test-4epochs.sh diff --git a/yarn-project/Earthfile b/yarn-project/Earthfile index 3016ebdaead..fb3cb799b63 100644 --- a/yarn-project/Earthfile +++ b/yarn-project/Earthfile @@ -283,6 +283,7 @@ prover-client-test: # Running this inside the main builder as the point is not to run this through dockerization. network-test: ARG test=./test-transfer.sh + ARG validators=3 FROM +build WORKDIR /usr/src/ # Bare minimum git setup to run 'git rev-parse --show-toplevel' @@ -299,7 +300,7 @@ network-test: ./ethereum.sh \ "./prover-node.sh 8078 false" \ ./pxe.sh \ - "./validators.sh 3" + "./validators.sh $validators" publish-npm: FROM +build diff --git a/yarn-project/aztec/package.json b/yarn-project/aztec/package.json index b30a23ae584..ee1ceb90931 100644 --- a/yarn-project/aztec/package.json +++ b/yarn-project/aztec/package.json @@ -55,6 +55,7 @@ "@aztec/telemetry-client": "workspace:^", "@aztec/txe": "workspace:^", "@aztec/types": "workspace:^", + "@opentelemetry/winston-transport": "^0.7.0", "@types/chalk": "^2.2.0", "abitype": "^0.8.11", "chalk": "^5.3.0", diff --git a/yarn-project/aztec/src/logging.ts b/yarn-project/aztec/src/logging.ts index d59cd9b33cd..a7deed55ae5 100644 --- a/yarn-project/aztec/src/logging.ts +++ b/yarn-project/aztec/src/logging.ts @@ -1,5 +1,6 @@ import { currentLevel, onLog, setLevel } from '@aztec/foundation/log'; +import { OpenTelemetryTransportV3 } from '@opentelemetry/winston-transport'; import * as path from 'path'; import * as process from 'process'; import * as winston from 'winston'; @@ -30,36 +31,15 @@ function createWinstonLocalFileLogger() { }); } -function extractNegativePatterns(debugString: string): string[] { - return ( - debugString - .split(',') - .filter(p => p.startsWith('-')) - // Remove the leading '-' from the pattern - .map(p => p.slice(1)) - // Remove any '*' from the pattern - .map(p => p.replace('*', '')) - ); -} - /** Creates a winston logger that logs everything to stdout in json format */ -function createWinstonJsonStdoutLogger( - debugString: string = process.env.DEBUG ?? - 'aztec:*,-aztec:avm_simulator*,-aztec:libp2p_service*,-aztec:circuits:artifact_hash,-json-rpc*', -) { - const ignorePatterns = extractNegativePatterns(debugString); - const ignoreAztecPattern = format(info => { - if (ignorePatterns.some(pattern => info.module.startsWith(pattern))) { - return false; // Skip logging this message - } - return info; - }); +function createWinstonJsonStdoutLogger() { return winston.createLogger({ level: currentLevel, transports: [ new winston.transports.Console({ - format: format.combine(format.timestamp(), ignoreAztecPattern(), format.json()), + format: format.combine(format.timestamp(), format.json()), }), + new OpenTelemetryTransportV3(), ], }); } diff --git a/yarn-project/end-to-end/scripts/native-network/boot-node.sh b/yarn-project/end-to-end/scripts/native-network/boot-node.sh index 5ee8e8e0f98..a266fe458d2 100755 --- a/yarn-project/end-to-end/scripts/native-network/boot-node.sh +++ b/yarn-project/end-to-end/scripts/native-network/boot-node.sh @@ -22,8 +22,9 @@ export P2P_TCP_ANNOUNCE_ADDR="127.0.0.1:40400" export P2P_UDP_ANNOUNCE_ADDR="127.0.0.1:40400" export P2P_TCP_LISTEN_ADDR="0.0.0.0:40400" export P2P_UDP_LISTEN_ADDR="0.0.0.0:40400" -export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT="" -export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="" +export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT="${OTEL_EXPORTER_OTLP_METRICS_ENDPOINT:-}" +export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:-}" +export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT="${OTEL_EXPORTER_OTLP_LOGS_ENDPOINT:-}" export VALIDATOR_PRIVATE_KEY="0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a" REPO=$(git rev-parse --show-toplevel) diff --git a/yarn-project/end-to-end/scripts/native-network/transaction-bot.sh b/yarn-project/end-to-end/scripts/native-network/transaction-bot.sh index f95306a2182..722bfdcf0ce 100755 --- a/yarn-project/end-to-end/scripts/native-network/transaction-bot.sh +++ b/yarn-project/end-to-end/scripts/native-network/transaction-bot.sh @@ -29,7 +29,6 @@ echo "Done waiting." # Set environment variables export ETHEREUM_HOST="http://127.0.0.1:8545" export AZTEC_NODE_URL="http://127.0.0.1:8080" -export LOG_JSON="1" export LOG_LEVEL=${LOG_LEVEL:-"debug"} export DEBUG="aztec:*,-aztec:avm_simulator*,-aztec:libp2p_service*,-aztec:circuits:artifact_hash,-json-rpc*,-aztec:l2_block_stream,-aztec:world-state:*" export BOT_PRIVATE_KEY="0xcafe" diff --git a/yarn-project/end-to-end/scripts/native-network/validator.sh b/yarn-project/end-to-end/scripts/native-network/validator.sh index 67750247ebd..53faa16e920 100755 --- a/yarn-project/end-to-end/scripts/native-network/validator.sh +++ b/yarn-project/end-to-end/scripts/native-network/validator.sh @@ -54,6 +54,9 @@ export P2P_TCP_ANNOUNCE_ADDR="127.0.0.1:$P2P_PORT" export P2P_UDP_ANNOUNCE_ADDR="127.0.0.1:$P2P_PORT" export P2P_TCP_LISTEN_ADDR="0.0.0.0:$P2P_PORT" export P2P_UDP_LISTEN_ADDR="0.0.0.0:$P2P_PORT" +export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT="${OTEL_EXPORTER_OTLP_METRICS_ENDPOINT:-}" +export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:-}" +export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT="${OTEL_EXPORTER_OTLP_LOGS_ENDPOINT:-}" # Add L1 validator # this may fail, so try 3 times diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index 06a7745487b..67b703b5c9a 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -58,6 +58,7 @@ export type EnvVar = | 'NOMISMATOKOPIO_CONTRACT_ADDRESS' | 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT' | 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT' + | 'OTEL_EXPORTER_OTLP_LOGS_ENDPOINT' | 'OTEL_SERVICE_NAME' | 'OUTBOX_CONTRACT_ADDRESS' | 'P2P_BLOCK_CHECK_INTERVAL_MS' diff --git a/yarn-project/foundation/src/log/logger.ts b/yarn-project/foundation/src/log/logger.ts index b3fe598e897..1730d83fb58 100644 --- a/yarn-project/foundation/src/log/logger.ts +++ b/yarn-project/foundation/src/log/logger.ts @@ -19,8 +19,24 @@ function getLogLevel() { export let currentLevel = getLogLevel(); +function filterNegativePatterns(debugString: string): string { + return debugString + .split(',') + .filter(p => !p.startsWith('-')) + .join(','); +} +function extractNegativePatterns(debugString: string): string[] { + return ( + debugString + .split(',') + .filter(p => p.startsWith('-')) + // Remove the leading '-' from the pattern + .map(p => p.slice(1)) + ); +} + const namespaces = process.env.DEBUG ?? 'aztec:*'; -debug.enable(namespaces); +debug.enable(filterNegativePatterns(namespaces)); /** Log function that accepts an exception object */ type ErrorLogFn = (msg: string, err?: Error | unknown, data?: LogData) => void; @@ -42,30 +58,57 @@ export type DebugLogger = Logger; * Uses npm debug for debug level and console.error for other levels. * @param name - Name of the module. * @param fixedLogData - Additional data to include in the log message. - * @usage createDebugLogger('aztec:validator', {validatorAddress: '0x1234...'}); + * @usage createDebugLogger('aztec:validator'); * // will always add the validator address to the log labels * @returns A debug logger. */ -export function createDebugLogger(name: string, fixedLogData?: LogData): DebugLogger { +export function createDebugLogger(name: string): DebugLogger { const debugLogger = debug(name); - const attatchFixedLogData = (data?: LogData) => ({ ...fixedLogData, ...data }); + const negativePatterns = extractNegativePatterns(namespaces); + const accepted = () => { + return !negativePatterns.some(pattern => name.match(pattern)); + }; + const log = (level: LogLevel, msg: string, data?: LogData) => { + if (accepted()) { + logWithDebug(debugLogger, level, msg, data); + } + }; + const logger = { + silent: () => {}, + error: (msg: string, err?: unknown, data?: LogData) => log('error', fmtErr(msg, err), data), + warn: (msg: string, data?: LogData) => log('warn', msg, data), + info: (msg: string, data?: LogData) => log('info', msg, data), + verbose: (msg: string, data?: LogData) => log('verbose', msg, data), + debug: (msg: string, data?: LogData) => log('debug', msg, data), + }; + return Object.assign((msg: string, data?: LogData) => log('debug', msg, data), logger); +} +/** + * A function to create a logger that automatically includes fixed data in each log entry. + * @param debugLogger - The base DebugLogger instance to which we attach fixed log data. + * @param fixedLogData - The data to be included in every log entry. + * @returns A DebugLogger with log level methods (error, warn, info, verbose, debug) that + * automatically attach `fixedLogData` to every log message. + */ +export function attachedFixedDataToLogger(debugLogger: DebugLogger, fixedLogData: LogData): DebugLogger { + // Helper function to merge fixed data with additional data passed to log entries. + const attach = (data?: LogData) => ({ ...fixedLogData, ...data }); + // Define the logger with all the necessary log level methods. const logger = { + // Silent log level does nothing. silent: () => {}, - error: (msg: string, err?: unknown, data?: LogData) => - logWithDebug(debugLogger, 'error', fmtErr(msg, err), attatchFixedLogData(data)), - warn: (msg: string, data?: LogData) => logWithDebug(debugLogger, 'warn', msg, attatchFixedLogData(data)), - info: (msg: string, data?: LogData) => logWithDebug(debugLogger, 'info', msg, attatchFixedLogData(data)), - verbose: (msg: string, data?: LogData) => logWithDebug(debugLogger, 'verbose', msg, attatchFixedLogData(data)), - debug: (msg: string, data?: LogData) => logWithDebug(debugLogger, 'debug', msg, attatchFixedLogData(data)), + error: (msg: string, err?: unknown, data?: LogData) => debugLogger.error(fmtErr(msg, err), attach(data)), + warn: (msg: string, data?: LogData) => debugLogger.warn(msg, attach(data)), + info: (msg: string, data?: LogData) => debugLogger.info(msg, attach(data)), + verbose: (msg: string, data?: LogData) => debugLogger.verbose(msg, attach(data)), + debug: (msg: string, data?: LogData) => debugLogger.debug(msg, attach(data)), }; - return Object.assign( - (msg: string, data?: LogData) => logWithDebug(debugLogger, 'debug', msg, attatchFixedLogData(data)), - logger, - ); + return Object.assign((msg: string, data?: LogData) => debugLogger.debug(msg, attach(data)), logger); } + /** A callback to capture all logs. */ export type LogHandler = (level: LogLevel, namespace: string, msg: string, data?: LogData) => void; diff --git a/yarn-project/telemetry-client/package.json b/yarn-project/telemetry-client/package.json index ecbc5444c34..8ebe92df672 100644 --- a/yarn-project/telemetry-client/package.json +++ b/yarn-project/telemetry-client/package.json @@ -28,14 +28,19 @@ "dependencies": { "@aztec/foundation": "workspace:^", "@opentelemetry/api": "^1.9.0", + "@opentelemetry/api-logs": "^0.54.0", + "@opentelemetry/exporter-logs-otlp-http": "^0.54.0", "@opentelemetry/exporter-metrics-otlp-http": "^0.52.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.52.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.54.0", "@opentelemetry/host-metrics": "^0.35.2", "@opentelemetry/resource-detector-aws": "^1.5.2", "@opentelemetry/resources": "^1.25.0", + "@opentelemetry/sdk-logs": "^0.54.0", "@opentelemetry/sdk-metrics": "^1.25.0", "@opentelemetry/sdk-trace-node": "^1.25.0", - "@opentelemetry/semantic-conventions": "^1.25.0" + "@opentelemetry/semantic-conventions": "^1.25.0", + "@opentelemetry/winston-transport": "^0.7.0", + "winston": "^3.10.0" }, "devDependencies": { "@jest/globals": "^29.5.0", diff --git a/yarn-project/telemetry-client/src/config.ts b/yarn-project/telemetry-client/src/config.ts index c7789ba05bc..c48a9be6bc4 100644 --- a/yarn-project/telemetry-client/src/config.ts +++ b/yarn-project/telemetry-client/src/config.ts @@ -3,6 +3,7 @@ import { type ConfigMappingsType, getConfigFromMappings } from '@aztec/foundatio export interface TelemetryClientConfig { metricsCollectorUrl?: URL; tracesCollectorUrl?: URL; + logsCollectorUrl?: URL; serviceName: string; networkName: string; } @@ -18,6 +19,11 @@ export const telemetryClientConfigMappings: ConfigMappingsType new URL(val), }, + logsCollectorUrl: { + env: 'OTEL_EXPORTER_OTLP_LOGS_ENDPOINT', + description: 'The URL of the telemetry collector for logs', + parseEnv: (val: string) => new URL(val), + }, serviceName: { env: 'OTEL_SERVICE_NAME', description: 'The URL of the telemetry collector', diff --git a/yarn-project/telemetry-client/src/otel.ts b/yarn-project/telemetry-client/src/otel.ts index 7e6ae406233..422fee32efe 100644 --- a/yarn-project/telemetry-client/src/otel.ts +++ b/yarn-project/telemetry-client/src/otel.ts @@ -20,11 +20,13 @@ import { processDetectorSync, serviceInstanceIdDetectorSync, } from '@opentelemetry/resources'; +import { type LoggerProvider } from '@opentelemetry/sdk-logs'; import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; import { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION } from '@opentelemetry/semantic-conventions'; import { aztecDetector } from './aztec_resource_detector.js'; +import { registerOtelLoggerProvider } from './otelLoggerProvider.js'; import { type Gauge, type TelemetryClient } from './telemetry.js'; export class OpenTelemetryClient implements TelemetryClient { @@ -35,6 +37,7 @@ export class OpenTelemetryClient implements TelemetryClient { private resource: IResource, private meterProvider: MeterProvider, private traceProvider: TracerProvider, + private loggerProvider: LoggerProvider, private log: DebugLogger, ) {} @@ -71,12 +74,13 @@ export class OpenTelemetryClient implements TelemetryClient { } public async stop() { - await Promise.all([this.meterProvider.shutdown()]); + await Promise.all([this.meterProvider.shutdown(), this.loggerProvider.shutdown()]); } public static async createAndStart( metricsCollector: URL, tracesCollector: URL | undefined, + logsCollector: URL | undefined, log: DebugLogger, ): Promise { const resource = detectResourcesSync({ @@ -116,8 +120,9 @@ export class OpenTelemetryClient implements TelemetryClient { }), ], }); + const loggerProvider = registerOtelLoggerProvider(logsCollector); - const service = new OpenTelemetryClient(resource, meterProvider, tracerProvider, log); + const service = new OpenTelemetryClient(resource, meterProvider, tracerProvider, loggerProvider, log); service.start(); return service; diff --git a/yarn-project/telemetry-client/src/otelLoggerProvider.ts b/yarn-project/telemetry-client/src/otelLoggerProvider.ts new file mode 100644 index 00000000000..0a0744c3501 --- /dev/null +++ b/yarn-project/telemetry-client/src/otelLoggerProvider.ts @@ -0,0 +1,19 @@ +import { logs as otelLogs } from '@opentelemetry/api-logs'; +import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'; +import { LoggerProvider, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs'; + +export function registerOtelLoggerProvider(otelLogsUrl?: URL) { + const loggerProvider = new LoggerProvider(); + if (!otelLogsUrl) { + // If no URL provided, return it disconnected. + return loggerProvider; + } + const logExporter = new OTLPLogExporter({ + url: otelLogsUrl.href, + }); + // Add a processor to the logger provider + loggerProvider.addLogRecordProcessor(new SimpleLogRecordProcessor(logExporter)); + otelLogs.setGlobalLoggerProvider(loggerProvider); + + return loggerProvider; +} diff --git a/yarn-project/telemetry-client/src/start.ts b/yarn-project/telemetry-client/src/start.ts index eb07a4a431b..c58692bb568 100644 --- a/yarn-project/telemetry-client/src/start.ts +++ b/yarn-project/telemetry-client/src/start.ts @@ -11,7 +11,12 @@ export async function createAndStartTelemetryClient(config: TelemetryClientConfi const log = createDebugLogger('aztec:telemetry-client'); if (config.metricsCollectorUrl) { log.info('Using OpenTelemetry client'); - return await OpenTelemetryClient.createAndStart(config.metricsCollectorUrl, config.tracesCollectorUrl, log); + return await OpenTelemetryClient.createAndStart( + config.metricsCollectorUrl, + config.tracesCollectorUrl, + config.logsCollectorUrl, + log, + ); } else { log.info('Using NoopTelemetryClient'); return new NoopTelemetryClient(); diff --git a/yarn-project/validator-client/src/validator.ts b/yarn-project/validator-client/src/validator.ts index 1c516b195dd..c7c70d950f0 100644 --- a/yarn-project/validator-client/src/validator.ts +++ b/yarn-project/validator-client/src/validator.ts @@ -2,7 +2,7 @@ import { type BlockAttestation, type BlockProposal, type TxHash } from '@aztec/c import { type Header } from '@aztec/circuits.js'; import { Buffer32 } from '@aztec/foundation/buffer'; import { type Fr } from '@aztec/foundation/fields'; -import { createDebugLogger } from '@aztec/foundation/log'; +import { attachedFixedDataToLogger, createDebugLogger } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; import { type P2P } from '@aztec/p2p'; import { type TelemetryClient, WithTracer } from '@aztec/telemetry-client'; @@ -40,7 +40,9 @@ export class ValidatorClient extends WithTracer implements Validator { private attestationPoolingIntervalMs: number, private attestationWaitTimeoutMs: number, telemetry: TelemetryClient, - private log = createDebugLogger('aztec:validator', { validatorAddress: keyStore.getAddress().toString() }), + private log = attachedFixedDataToLogger(createDebugLogger('aztec:validator'), { + validatorAddress: keyStore.getAddress().toString(), + }), ) { // Instantiate tracer super(telemetry, 'Validator'); diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 567d78868ad..a20dfb4d126 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -260,6 +260,7 @@ __metadata: "@aztec/txe": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 + "@opentelemetry/winston-transport": ^0.7.0 "@types/chalk": ^2.2.0 "@types/jest": ^29.5.0 "@types/koa": ^2.13.6 @@ -1151,18 +1152,23 @@ __metadata: "@aztec/foundation": "workspace:^" "@jest/globals": ^29.5.0 "@opentelemetry/api": ^1.9.0 + "@opentelemetry/api-logs": ^0.54.0 + "@opentelemetry/exporter-logs-otlp-http": ^0.54.0 "@opentelemetry/exporter-metrics-otlp-http": ^0.52.0 - "@opentelemetry/exporter-trace-otlp-http": ^0.52.0 + "@opentelemetry/exporter-trace-otlp-http": ^0.54.0 "@opentelemetry/host-metrics": ^0.35.2 "@opentelemetry/resource-detector-aws": ^1.5.2 "@opentelemetry/resources": ^1.25.0 + "@opentelemetry/sdk-logs": ^0.54.0 "@opentelemetry/sdk-metrics": ^1.25.0 "@opentelemetry/sdk-trace-node": ^1.25.0 "@opentelemetry/semantic-conventions": ^1.25.0 + "@opentelemetry/winston-transport": ^0.7.0 "@types/jest": ^29.5.0 jest: ^29.5.0 ts-node: ^10.9.1 typescript: ^5.0.4 + winston: ^3.10.0 languageName: unknown linkType: soft @@ -3445,7 +3451,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/api@npm:^1.0.0, @opentelemetry/api@npm:^1.9.0": +"@opentelemetry/api-logs@npm:0.54.0, @opentelemetry/api-logs@npm:^0.54.0": + version: 0.54.0 + resolution: "@opentelemetry/api-logs@npm:0.54.0" + dependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 5fc91054a290663844049cd9eb66419ea06d191b82220f2513147acdbd82579d1d3703a7e09f58a0014118d52b96d8b6340f9b43dd33a2c4469a31f13b3abc62 + languageName: node + linkType: hard + +"@opentelemetry/api@npm:^1.0.0, @opentelemetry/api@npm:^1.3.0, @opentelemetry/api@npm:^1.9.0": version: 1.9.0 resolution: "@opentelemetry/api@npm:1.9.0" checksum: 9e88e59d53ced668f3daaecfd721071c5b85a67dd386f1c6f051d1be54375d850016c881f656ffbe9a03bedae85f7e89c2f2b635313f9c9b195ad033cdc31020 @@ -3483,6 +3498,32 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/core@npm:1.27.0": + version: 1.27.0 + resolution: "@opentelemetry/core@npm:1.27.0" + dependencies: + "@opentelemetry/semantic-conventions": 1.27.0 + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 33ff551f89f0bb95830c9f9464c43b11adf88882ec1d3a03a5b9afcc89d2aafab33c36cb5047f18667d7929d6ab40ed0121649c42d0105f1cb33ffdca48f8b13 + languageName: node + linkType: hard + +"@opentelemetry/exporter-logs-otlp-http@npm:^0.54.0": + version: 0.54.0 + resolution: "@opentelemetry/exporter-logs-otlp-http@npm:0.54.0" + dependencies: + "@opentelemetry/api-logs": 0.54.0 + "@opentelemetry/core": 1.27.0 + "@opentelemetry/otlp-exporter-base": 0.54.0 + "@opentelemetry/otlp-transformer": 0.54.0 + "@opentelemetry/sdk-logs": 0.54.0 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 407cde2dd930aa19c0c826147d15aba84f94a58f1afbf86cfa1c41576be4492b689e1e9c7971a92805b051851cd6fab063bf24f29160b14c2d3b2cf1fded2bec + languageName: node + linkType: hard + "@opentelemetry/exporter-metrics-otlp-http@npm:^0.52.0": version: 0.52.0 resolution: "@opentelemetry/exporter-metrics-otlp-http@npm:0.52.0" @@ -3498,18 +3539,18 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-http@npm:^0.52.0": - version: 0.52.0 - resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.52.0" +"@opentelemetry/exporter-trace-otlp-http@npm:^0.54.0": + version: 0.54.0 + resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.54.0" dependencies: - "@opentelemetry/core": 1.25.0 - "@opentelemetry/otlp-exporter-base": 0.52.0 - "@opentelemetry/otlp-transformer": 0.52.0 - "@opentelemetry/resources": 1.25.0 - "@opentelemetry/sdk-trace-base": 1.25.0 + "@opentelemetry/core": 1.27.0 + "@opentelemetry/otlp-exporter-base": 0.54.0 + "@opentelemetry/otlp-transformer": 0.54.0 + "@opentelemetry/resources": 1.27.0 + "@opentelemetry/sdk-trace-base": 1.27.0 peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: bed18523289c579b8108b1c3fcb2b74361bed2d7f3016270feb080a047fa422fc9dfb0678ff1b726cb1e0fa9413cead5824e7f97d1d781467aa983a87fe1ee93 + "@opentelemetry/api": ^1.3.0 + checksum: e53263c3ffcfe62d7d299efac9515a977d284aabc4c89a961cec60853095f24e439abae348c326c7bd88911a85d08dd57833a64769e20254d02df8ac73e9b277 languageName: node linkType: hard @@ -3537,6 +3578,18 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/otlp-exporter-base@npm:0.54.0": + version: 0.54.0 + resolution: "@opentelemetry/otlp-exporter-base@npm:0.54.0" + dependencies: + "@opentelemetry/core": 1.27.0 + "@opentelemetry/otlp-transformer": 0.54.0 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: ded78325f22cd98314971216eb18d8f021a6cf7f3b1f69d08b0d257880deb2d409d598bfc3a6016b0557a1ec3b0c50527ba9acf09d4e3902f48d003f763441c0 + languageName: node + linkType: hard + "@opentelemetry/otlp-transformer@npm:0.52.0": version: 0.52.0 resolution: "@opentelemetry/otlp-transformer@npm:0.52.0" @@ -3554,6 +3607,23 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/otlp-transformer@npm:0.54.0": + version: 0.54.0 + resolution: "@opentelemetry/otlp-transformer@npm:0.54.0" + dependencies: + "@opentelemetry/api-logs": 0.54.0 + "@opentelemetry/core": 1.27.0 + "@opentelemetry/resources": 1.27.0 + "@opentelemetry/sdk-logs": 0.54.0 + "@opentelemetry/sdk-metrics": 1.27.0 + "@opentelemetry/sdk-trace-base": 1.27.0 + protobufjs: ^7.3.0 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 69451290ec2c65ee27f35b29d41a1b961d169ff928d231805c2694cbc4b4bda788027cf8149a6a1325da7c3bc2ca20dc939ef91a4f3e2af481ed187653386610 + languageName: node + linkType: hard + "@opentelemetry/propagator-b3@npm:1.25.0": version: 1.25.0 resolution: "@opentelemetry/propagator-b3@npm:1.25.0" @@ -3601,6 +3671,18 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/resources@npm:1.27.0": + version: 1.27.0 + resolution: "@opentelemetry/resources@npm:1.27.0" + dependencies: + "@opentelemetry/core": 1.27.0 + "@opentelemetry/semantic-conventions": 1.27.0 + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 43d298afea7daf7524e6b98c1441bcce9fa73b76aecf17e36cabb1a4cfaae6818acf9759d3e42706b1fd91243644076d2291e78c3ed81641d3b351fcff6cb9a9 + languageName: node + linkType: hard + "@opentelemetry/resources@npm:^1.0.0": version: 1.25.1 resolution: "@opentelemetry/resources@npm:1.25.1" @@ -3626,6 +3708,19 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/sdk-logs@npm:0.54.0, @opentelemetry/sdk-logs@npm:^0.54.0": + version: 0.54.0 + resolution: "@opentelemetry/sdk-logs@npm:0.54.0" + dependencies: + "@opentelemetry/api-logs": 0.54.0 + "@opentelemetry/core": 1.27.0 + "@opentelemetry/resources": 1.27.0 + peerDependencies: + "@opentelemetry/api": ">=1.4.0 <1.10.0" + checksum: fd6db65af6d7afdb454eac1df8a4029d3d287d37e9289a4d128bea07995e8843b7b1e5d1f39aa39538397ce1b6bf624cc2548f40dc18324ba3bbaec86dd845b9 + languageName: node + linkType: hard + "@opentelemetry/sdk-metrics@npm:1.25.0, @opentelemetry/sdk-metrics@npm:^1.25.0, @opentelemetry/sdk-metrics@npm:^1.8.0": version: 1.25.0 resolution: "@opentelemetry/sdk-metrics@npm:1.25.0" @@ -3639,6 +3734,18 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/sdk-metrics@npm:1.27.0": + version: 1.27.0 + resolution: "@opentelemetry/sdk-metrics@npm:1.27.0" + dependencies: + "@opentelemetry/core": 1.27.0 + "@opentelemetry/resources": 1.27.0 + peerDependencies: + "@opentelemetry/api": ">=1.3.0 <1.10.0" + checksum: c8776577063a3a5199d5717247270daf5820ce6636530b5ea4b5a8d6b40170cec9bb6b56dacb5c118d2e90588af83d0ebbb13f4d370c7efe50f69d22e5d13463 + languageName: node + linkType: hard + "@opentelemetry/sdk-trace-base@npm:1.25.0": version: 1.25.0 resolution: "@opentelemetry/sdk-trace-base@npm:1.25.0" @@ -3652,6 +3759,19 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/sdk-trace-base@npm:1.27.0": + version: 1.27.0 + resolution: "@opentelemetry/sdk-trace-base@npm:1.27.0" + dependencies: + "@opentelemetry/core": 1.27.0 + "@opentelemetry/resources": 1.27.0 + "@opentelemetry/semantic-conventions": 1.27.0 + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: d28c36724aeaf4884f7957e2ab138d9a0ca715a68b2ad23e2935ff0e39cd438c57fd0c8cc85fd5e280464857ede1ae8f9c8e40a37088a1e34d2e625e77276fee + languageName: node + linkType: hard + "@opentelemetry/sdk-trace-node@npm:^1.25.0": version: 1.25.0 resolution: "@opentelemetry/sdk-trace-node@npm:1.25.0" @@ -3682,6 +3802,23 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/semantic-conventions@npm:1.27.0": + version: 1.27.0 + resolution: "@opentelemetry/semantic-conventions@npm:1.27.0" + checksum: 26d85f8d13c8c64024f7a84528cff41d56afc9829e7ff8a654576404f8b2c1a9c264adcc6fa5a9551bacdd938a4a464041fa9493e0a722e5605f2c2ae6752398 + languageName: node + linkType: hard + +"@opentelemetry/winston-transport@npm:^0.7.0": + version: 0.7.0 + resolution: "@opentelemetry/winston-transport@npm:0.7.0" + dependencies: + "@opentelemetry/api-logs": ^0.54.0 + winston-transport: 4.* + checksum: a75d1915e90ab9beaec842fe2f2ce053ea2b43001d8be7cfd47945fa6e1dee6e1d1b5850becb72c9553edb6904844b685df838a1a2cbea0f2f6edf6ce85dc3bb + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -5317,6 +5454,15 @@ __metadata: languageName: node linkType: hard +"abort-controller@npm:^3.0.0": + version: 3.0.0 + resolution: "abort-controller@npm:3.0.0" + dependencies: + event-target-shim: ^5.0.0 + checksum: 170bdba9b47b7e65906a28c8ce4f38a7a369d78e2271706f020849c1bfe0ee2067d4261df8bbb66eb84f79208fd5b710df759d64191db58cfba7ce8ef9c54b75 + languageName: node + linkType: hard + "abortable-iterator@npm:^5.0.1": version: 5.0.1 resolution: "abortable-iterator@npm:5.0.1" @@ -8612,6 +8758,13 @@ __metadata: languageName: node linkType: hard +"event-target-shim@npm:^5.0.0": + version: 5.0.1 + resolution: "event-target-shim@npm:5.0.1" + checksum: 1ffe3bb22a6d51bdeb6bf6f7cf97d2ff4a74b017ad12284cc9e6a279e727dc30a5de6bb613e5596ff4dc3e517841339ad09a7eec44266eccb1aa201a30448166 + languageName: node + linkType: hard + "eventemitter3@npm:^4.0.0": version: 4.0.7 resolution: "eventemitter3@npm:4.0.7" @@ -11779,6 +11932,20 @@ __metadata: languageName: node linkType: hard +"logform@npm:^2.6.1": + version: 2.6.1 + resolution: "logform@npm:2.6.1" + dependencies: + "@colors/colors": 1.6.0 + "@types/triple-beam": ^1.3.2 + fecha: ^4.2.0 + ms: ^2.1.1 + safe-stable-stringify: ^2.3.1 + triple-beam: ^1.3.0 + checksum: 0c6b95fa8350ccc33c7c33d77de2a9920205399706fc1b125151c857b61eb90873f4670d9e0e58e58c165b68a363206ae670d6da8b714527c838da3c84449605 + languageName: node + linkType: hard + "long@npm:^5.0.0": version: 5.2.3 resolution: "long@npm:5.2.3" @@ -13838,6 +14005,19 @@ __metadata: languageName: node linkType: hard +"readable-stream@npm:^4.5.2": + version: 4.5.2 + resolution: "readable-stream@npm:4.5.2" + dependencies: + abort-controller: ^3.0.0 + buffer: ^6.0.3 + events: ^3.3.0 + process: ^0.11.10 + string_decoder: ^1.3.0 + checksum: c4030ccff010b83e4f33289c535f7830190773e274b3fcb6e2541475070bdfd69c98001c3b0cb78763fc00c8b62f514d96c2b10a8bd35d5ce45203a25fa1d33a + languageName: node + linkType: hard + "receptacle@npm:^1.3.2": version: 1.3.2 resolution: "receptacle@npm:1.3.2" @@ -14871,7 +15051,7 @@ __metadata: languageName: node linkType: hard -"string_decoder@npm:^1.1.1": +"string_decoder@npm:^1.1.1, string_decoder@npm:^1.3.0": version: 1.3.0 resolution: "string_decoder@npm:1.3.0" dependencies: @@ -16323,6 +16503,17 @@ __metadata: languageName: node linkType: hard +"winston-transport@npm:4.*": + version: 4.8.0 + resolution: "winston-transport@npm:4.8.0" + dependencies: + logform: ^2.6.1 + readable-stream: ^4.5.2 + triple-beam: ^1.3.0 + checksum: f84092188176d49a6f4f75321ba3e50107ac0942a51a6d7e36b80af19dafb22b57258aaa6d8220763044ea23e30bffd597d3280d2a2298e6a491fe424896bac7 + languageName: node + linkType: hard + "winston-transport@npm:^4.4.0, winston-transport@npm:^4.7.0": version: 4.7.0 resolution: "winston-transport@npm:4.7.0"