diff --git a/apps/otlp.go b/apps/otlp.go index 6ceb70e637..e2887fc6a0 100644 --- a/apps/otlp.go +++ b/apps/otlp.go @@ -137,7 +137,7 @@ func (r ReceiverOTLP) Pipelines(ctx context.Context) ([]otel.ReceiverPipeline, e receiverPipelineType, metricsRDM, metricsProcessors := r.metricsProcessors(ctx) - return []otel.ReceiverPipeline{{ + return []otel.ReceiverPipeline{confgenerator.ConvertToOtlpExporter(otel.ReceiverPipeline{ ExporterTypes: map[string]otel.ExporterType{ "metrics": receiverPipelineType, "traces": otel.OTel, @@ -163,7 +163,7 @@ func (r ReceiverOTLP) Pipelines(ctx context.Context) ([]otel.ReceiverPipeline, e "traces": otel.SetIfMissing, "logs": otel.SetIfMissing, }, - }}, nil + }, ctx, false, false)}, nil } func init() { diff --git a/confgenerator/confgenerator.go b/confgenerator/confgenerator.go index d5d0db869c..e5f4cb0c1f 100644 --- a/confgenerator/confgenerator.go +++ b/confgenerator/confgenerator.go @@ -75,23 +75,32 @@ func ConvertToOtlpExporter(pipeline otel.ReceiverPipeline, ctx context.Context, if !expOtlpExporter { return pipeline } - _, err := pipeline.ExporterTypes["metrics"] - if !err { - return pipeline - } - pipeline.ExporterTypes["metrics"] = otel.OTLP - pipeline.Processors["metrics"] = append(pipeline.Processors["metrics"], otel.GCPProjectID(resource.ProjectName())) - if isSystem { - pipeline.Processors["metrics"] = append(pipeline.Processors["metrics"], otel.MetricsRemoveInstrumentationLibraryLabelsAttributes()) - pipeline.Processors["metrics"] = append(pipeline.Processors["metrics"], otel.MetricsRemoveServiceAttributes()) + if _, ok := pipeline.ExporterTypes["metrics"]; ok { + pipeline.ExporterTypes["metrics"] = otel.OTLP + pipeline.Processors["metrics"] = append(pipeline.Processors["metrics"], otel.GCPProjectID(resource.ProjectName())) + if isSystem { + pipeline.Processors["metrics"] = append(pipeline.Processors["metrics"], otel.MetricsRemoveInstrumentationLibraryLabelsAttributes()) + pipeline.Processors["metrics"] = append(pipeline.Processors["metrics"], otel.MetricsRemoveServiceAttributes()) + } + // The OTLP exporter doesn't batch by default like the googlecloud.* exporters. We need this to avoid the API point limits. + pipeline.Processors["metrics"] = append(pipeline.Processors["metrics"], otel.Batch()) + if isPrometheus { + pipeline.Processors["metrics"] = append(pipeline.Processors["metrics"], otel.MetricUnknownCounter()) + pipeline.Processors["metrics"] = append(pipeline.Processors["metrics"], otel.MetricStartTime()) + } } - // The OTLP exporter doesn't batch by default like the googlecloud.* exporters. We need this to avoid the API point limits. - pipeline.Processors["metrics"] = append(pipeline.Processors["metrics"], otel.Batch()) - if isPrometheus { - pipeline.Processors["metrics"] = append(pipeline.Processors["metrics"], otel.MetricUnknownCounter()) - pipeline.Processors["metrics"] = append(pipeline.Processors["metrics"], otel.MetricStartTime()) + if _, ok := pipeline.ExporterTypes["logs"]; ok { + pipeline.ExporterTypes["logs"] = otel.OTLPStagingUTR + pipeline.Processors["logs"] = append(pipeline.Processors["logs"], otel.GCPProjectID(resource.ProjectName())) + pipeline.Processors["logs"] = append(pipeline.Processors["logs"], otel.Batch()) + pipeline.Processors["logs"] = append(pipeline.Processors["logs"], otel.DisableOtlpRoundTrip()) + pipeline.Processors["logs"] = append(pipeline.Processors["logs"], otel.InstrumentationScope()) + pipeline.Processors["logs"] = append(pipeline.Processors["logs"], otel.CopyServiceResourceLabels()) + pipeline.Processors["logs"] = append(pipeline.Processors["logs"], otel.ConvertSeverityTextToLowercase()) + pipeline.Processors["logs"] = append(pipeline.Processors["logs"], otel.AddResourceType()) } + return pipeline } @@ -110,6 +119,23 @@ func otlpExporter(userAgent string) otel.Component { } } +// This will merge with the otlp exporter above once the prod UTR logging endpoint is ready. +func otlpExporterUTRLoggingStaging(userAgent string) otel.Component { + return otel.Component{ + Type: "otlphttp", + Config: map[string]interface{}{ + "endpoint": "https://test-us-central2-telemetry.sandbox.googleapis.com", + "auth": map[string]interface{}{ + "authenticator": "googleclientauth", + }, + "headers": map[string]string{ + "User-Agent": userAgent, + }, + "encoding": "json", + }, + } +} + func googleManagedPrometheusExporter(userAgent string) otel.Component { return otel.Component{ Type: "googlemanagedprometheus", @@ -165,10 +191,11 @@ func (uc *UnifiedConfig) GenerateOtelConfig(ctx context.Context, outDir string) Pipelines: pipelines, Extensions: extensions, Exporters: map[otel.ExporterType]otel.Component{ - otel.System: googleCloudExporter(userAgent, false, false), - otel.OTel: googleCloudExporter(userAgent, true, true), - otel.GMP: googleManagedPrometheusExporter(userAgent), - otel.OTLP: otlpExporter(userAgent), + otel.System: googleCloudExporter(userAgent, false, false), + otel.OTel: googleCloudExporter(userAgent, true, true), + otel.GMP: googleManagedPrometheusExporter(userAgent), + otel.OTLP: otlpExporter(userAgent), + otel.OTLPStagingUTR: otlpExporterUTRLoggingStaging(userAgent), }, }.Generate(ctx) if err != nil { diff --git a/confgenerator/logging_receivers.go b/confgenerator/logging_receivers.go index 6bece3f0b3..a2f43b97b0 100644 --- a/confgenerator/logging_receivers.go +++ b/confgenerator/logging_receivers.go @@ -232,7 +232,7 @@ func (r LoggingReceiverFilesMixin) Pipelines(ctx context.Context) ([]otel.Receiv }) } receiver_config["operators"] = operators - return []otel.ReceiverPipeline{{ + return []otel.ReceiverPipeline{ConvertToOtlpExporter(otel.ReceiverPipeline{ Receiver: otel.Component{ Type: "filelog", Config: receiver_config, @@ -243,7 +243,7 @@ func (r LoggingReceiverFilesMixin) Pipelines(ctx context.Context) ([]otel.Receiv ExporterTypes: map[string]otel.ExporterType{ "logs": otel.OTel, }, - }}, nil + }, ctx, false, false)}, nil } func (r LoggingReceiverFilesMixin) MergeInternalLoggingProcessor(p InternalLoggingProcessor) (InternalLoggingReceiver, InternalLoggingProcessor) { @@ -356,7 +356,7 @@ func (r LoggingReceiverSyslog) Pipelines(ctx context.Context) ([]otel.ReceiverPi "protocol": "rfc5424", } - return []otel.ReceiverPipeline{{ + return []otel.ReceiverPipeline{ConvertToOtlpExporter(otel.ReceiverPipeline{ Receiver: otel.Component{ Type: "syslog", Config: config, @@ -368,7 +368,7 @@ func (r LoggingReceiverSyslog) Pipelines(ctx context.Context) ([]otel.ReceiverPi ExporterTypes: map[string]otel.ExporterType{ "logs": otel.OTel, }, - }}, nil + }, ctx, false, false)}, nil } func init() { @@ -1001,7 +1001,7 @@ func (r LoggingReceiverSystemd) Pipelines(ctx context.Context) ([]otel.ReceiverP return nil, err } - return []otel.ReceiverPipeline{{ + return []otel.ReceiverPipeline{ConvertToOtlpExporter(otel.ReceiverPipeline{ Receiver: otel.Component{ Type: "journald", Config: receiver_config, @@ -1013,7 +1013,7 @@ func (r LoggingReceiverSystemd) Pipelines(ctx context.Context) ([]otel.ReceiverP ExporterTypes: map[string]otel.ExporterType{ "logs": otel.OTel, }, - }}, nil + }, ctx, false, false)}, nil } func init() { diff --git a/confgenerator/otel/modular.go b/confgenerator/otel/modular.go index c0cd79f851..32124081cb 100644 --- a/confgenerator/otel/modular.go +++ b/confgenerator/otel/modular.go @@ -38,6 +38,7 @@ const ( System GMP OTLP + OTLPStagingUTR ) const ( Override ResourceDetectionMode = iota @@ -53,6 +54,8 @@ func (t ExporterType) Name() string { return "otel" } else if t == OTLP { return "otlp" + } else if t == OTLPStagingUTR { + return "otlp-utr-logging" } else { panic("unknown ExporterType") } diff --git a/confgenerator/otel/ottl/ottl.go b/confgenerator/otel/ottl/ottl.go index e16e6d3452..8fa9ae8e12 100644 --- a/confgenerator/otel/ottl/ottl.go +++ b/confgenerator/otel/ottl/ottl.go @@ -234,6 +234,10 @@ func IsNotNil(a Value) Value { return valuef(`%s != nil`, a) } +func IsNotEmptyString(a Value) Value { + return valuef(`%s != ""`, a) +} + // ExtractCountMetric creates a new metric based on the count value of a Histogram metric func ExtractCountMetric(monotonic bool, metricName string) Statements { monotonicStr := "false" diff --git a/confgenerator/otel/processors.go b/confgenerator/otel/processors.go index deb1009d06..6e996a1f24 100644 --- a/confgenerator/otel/processors.go +++ b/confgenerator/otel/processors.go @@ -676,6 +676,19 @@ func GCPProjectID(projectID string) Component { ) } +// temp processor, will delete when UTR logging recognizes cloud.platform resource attribute +func AddResourceType() Component { + return ResourceTransform( + map[string]string{"gcp.resource_type": "gce_instance"}, false, + ) +} + +func DisableOtlpRoundTrip() Component { + return ResourceTransform( + map[string]string{"gcp.internal.omit_otlp": "true"}, false, + ) +} + // MetricUnknownCounter is necessary to handle prometheus unknown type metrics // go/ops-agent-otlp-migration func MetricUnknownCounter() Component { @@ -689,6 +702,39 @@ func MetricUnknownCounter() Component { }) } +func InstrumentationScope() Component { + return Transform("log", "log", ottl.NewStatements( + ottl.LValue{"attributes", "instrumentation_source"}.SetIf(ottl.RValue("instrumentation_scope.name"), ottl.IsNotEmptyString(ottl.RValue("instrumentation_scope.name"))), + ottl.LValue{"attributes", "instrumentation_version"}.SetIf(ottl.RValue("instrumentation_scope.version"), ottl.IsNotEmptyString(ottl.RValue("instrumentation_scope.version"))), + )) +} + +func FlattenSourceLocation() Component { + return Transform("log", "log", ottl.NewStatements( + ottl.LValue{"attributes", "code.file.path"}.SetIf(ottl.RValue(`attributes["gcp.source_location"]["file"]`), ottl.IsNotNil(ottl.RValue(`attributes["gcp.source_location"]["file"]`))), + ottl.LValue{"attributes", "code.function.name"}.SetIf(ottl.RValue(`attributes["gcp.source_location"]["func"]`), ottl.IsNotNil(ottl.RValue(`attributes["gcp.source_location"]["func"]`))), + ottl.LValue{"attributes", "code.line.number"}.SetIf(ottl.RValue(`attributes["gcp.source_location"]["line"]`), ottl.IsNotNil(ottl.RValue(`attributes["gcp.source_location"]["line"]`))), + ottl.LValue{"attributes", "gcp.source_location"}.Delete(), + )) +} + +// This processor copies the service.* attributes from the resource to the log attributes, if they exist. +func CopyServiceResourceLabels() Component { + return Transform("log", "log", ottl.NewStatements( + ottl.LValue{"attributes", "service.name"}.SetIf(ottl.RValue(`resource.attributes["service.name"]`), ottl.IsNotNil(ottl.RValue(`resource.attributes["service.name"]`))), + ottl.LValue{"attributes", "service.namespace"}.SetIf(ottl.RValue(`resource.attributes["service.namespace"]`), ottl.IsNotNil(ottl.RValue(`resource.attributes["service.namespace"]`))), + ottl.LValue{"attributes", "service.instance.id"}.SetIf(ottl.RValue(`resource.attributes["service.instance.id"]`), ottl.IsNotNil(ottl.RValue(`resource.attributes["service.instance.id"]`))), + )) +} + +// A temp processor, will delete when UTR logging supports case-insensitive severity text parsing. +func ConvertSeverityTextToLowercase() Component { + return Transform("log", "log", []ottl.Statement{ + `set(severity_text, ToLowerCase(severity_text)) where severity_text != nil`, + }) + +} + func Batch() Component { return Component{ Type: "batch", diff --git a/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/linux-gpu/otel.yaml b/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/linux-gpu/otel.yaml index 6c890e564f..4a5ce4b42b 100644 --- a/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/linux-gpu/otel.yaml +++ b/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/linux-gpu/otel.yaml @@ -15,10 +15,6 @@ exporters: service_resource_labels: true skip_create_descriptor: true user_agent: Google-Cloud-Ops-Agent-Metrics/latest (BuildDistro=build_distro;Platform=linux;ShortName=linux_platform;ShortVersion=linux_platform_version) - googlemanagedprometheus: - metric: - add_metric_suffixes: false - user_agent: Google-Cloud-Ops-Agent-Metrics/latest (BuildDistro=build_distro;Platform=linux;ShortName=linux_platform;ShortVersion=linux_platform_version) otlphttp/otlp: auth: authenticator: googleclientauth @@ -37,6 +33,9 @@ processors: batch/hostmetrics_6: send_batch_max_size: 200 send_batch_size: 200 + batch/otlp_4: + send_batch_max_size: 200 + send_batch_size: 200 cumulativetodelta/loggingmetrics_4: include: match_type: strict @@ -618,6 +617,11 @@ processors: - action: insert key: gcp.project_id value: test-project + resource/otlp_3: + attributes: + - action: insert + key: gcp.project_id + value: test-project resourcedetection/_global_0: detectors: - gcp @@ -834,11 +838,13 @@ service: - prometheus/agent_prometheus metrics/otlp_otlp: exporters: - - googlemanagedprometheus + - otlphttp/otlp processors: - resourcedetection/otlp_0 - transform/otlp_1 - groupbyattrs/otlp_2 + - resource/otlp_3 + - batch/otlp_4 receivers: - otlp/otlp traces/traces_otlp_otlp: diff --git a/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/linux/otel.yaml b/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/linux/otel.yaml index 9e3c11a338..f9156189a9 100644 --- a/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/linux/otel.yaml +++ b/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/linux/otel.yaml @@ -15,10 +15,6 @@ exporters: service_resource_labels: true skip_create_descriptor: true user_agent: Google-Cloud-Ops-Agent-Metrics/latest (BuildDistro=build_distro;Platform=linux;ShortName=linux_platform;ShortVersion=linux_platform_version) - googlemanagedprometheus: - metric: - add_metric_suffixes: false - user_agent: Google-Cloud-Ops-Agent-Metrics/latest (BuildDistro=build_distro;Platform=linux;ShortName=linux_platform;ShortVersion=linux_platform_version) otlphttp/otlp: auth: authenticator: googleclientauth @@ -34,6 +30,9 @@ processors: batch/hostmetrics_6: send_batch_max_size: 200 send_batch_size: 200 + batch/otlp_4: + send_batch_max_size: 200 + send_batch_size: 200 cumulativetodelta/loggingmetrics_4: include: match_type: strict @@ -581,6 +580,11 @@ processors: - action: insert key: gcp.project_id value: test-project + resource/otlp_3: + attributes: + - action: insert + key: gcp.project_id + value: test-project resourcedetection/_global_0: detectors: - gcp @@ -767,11 +771,13 @@ service: - prometheus/agent_prometheus metrics/otlp_otlp: exporters: - - googlemanagedprometheus + - otlphttp/otlp processors: - resourcedetection/otlp_0 - transform/otlp_1 - groupbyattrs/otlp_2 + - resource/otlp_3 + - batch/otlp_4 receivers: - otlp/otlp traces/traces_otlp_otlp: diff --git a/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/windows-2012/otel.yaml b/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/windows-2012/otel.yaml index e908281da8..e6d470ac44 100644 --- a/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/windows-2012/otel.yaml +++ b/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/windows-2012/otel.yaml @@ -15,10 +15,6 @@ exporters: service_resource_labels: true skip_create_descriptor: true user_agent: Google-Cloud-Ops-Agent-Metrics/latest (BuildDistro=build_distro;Platform=windows;ShortName=win_platform;ShortVersion=win_platform_version) - googlemanagedprometheus: - metric: - add_metric_suffixes: false - user_agent: Google-Cloud-Ops-Agent-Metrics/latest (BuildDistro=build_distro;Platform=windows;ShortName=win_platform;ShortVersion=win_platform_version) otlphttp/otlp: auth: authenticator: googleclientauth @@ -40,6 +36,9 @@ processors: batch/mssql_5: send_batch_max_size: 200 send_batch_size: 200 + batch/otlp_4: + send_batch_max_size: 200 + send_batch_size: 200 casttosum/iis_1: metrics: - agent.googleapis.com/iis/network/transferred_bytes_count @@ -663,6 +662,11 @@ processors: - action: insert key: gcp.project_id value: test-project + resource/otlp_3: + attributes: + - action: insert + key: gcp.project_id + value: test-project resourcedetection/_global_0: detectors: - gcp @@ -956,11 +960,13 @@ service: - prometheus/agent_prometheus metrics/otlp_otlp: exporters: - - googlemanagedprometheus + - otlphttp/otlp processors: - resourcedetection/otlp_0 - transform/otlp_1 - groupbyattrs/otlp_2 + - resource/otlp_3 + - batch/otlp_4 receivers: - otlp/otlp traces/traces_otlp_otlp: diff --git a/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/windows/otel.yaml b/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/windows/otel.yaml index e908281da8..e6d470ac44 100644 --- a/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/windows/otel.yaml +++ b/confgenerator/testdata/goldens/combined-receiver_otlp_otlphttp/golden/windows/otel.yaml @@ -15,10 +15,6 @@ exporters: service_resource_labels: true skip_create_descriptor: true user_agent: Google-Cloud-Ops-Agent-Metrics/latest (BuildDistro=build_distro;Platform=windows;ShortName=win_platform;ShortVersion=win_platform_version) - googlemanagedprometheus: - metric: - add_metric_suffixes: false - user_agent: Google-Cloud-Ops-Agent-Metrics/latest (BuildDistro=build_distro;Platform=windows;ShortName=win_platform;ShortVersion=win_platform_version) otlphttp/otlp: auth: authenticator: googleclientauth @@ -40,6 +36,9 @@ processors: batch/mssql_5: send_batch_max_size: 200 send_batch_size: 200 + batch/otlp_4: + send_batch_max_size: 200 + send_batch_size: 200 casttosum/iis_1: metrics: - agent.googleapis.com/iis/network/transferred_bytes_count @@ -663,6 +662,11 @@ processors: - action: insert key: gcp.project_id value: test-project + resource/otlp_3: + attributes: + - action: insert + key: gcp.project_id + value: test-project resourcedetection/_global_0: detectors: - gcp @@ -956,11 +960,13 @@ service: - prometheus/agent_prometheus metrics/otlp_otlp: exporters: - - googlemanagedprometheus + - otlphttp/otlp processors: - resourcedetection/otlp_0 - transform/otlp_1 - groupbyattrs/otlp_2 + - resource/otlp_3 + - batch/otlp_4 receivers: - otlp/otlp traces/traces_otlp_otlp: diff --git a/integration_test/agents/agents.go b/integration_test/agents/agents.go index 1a7f12bc1e..555044b9cc 100644 --- a/integration_test/agents/agents.go +++ b/integration_test/agents/agents.go @@ -1236,6 +1236,7 @@ const ( OtelLoggingFeatureFlag = "otel_logging" OtlpHttpExporterFeatureFlag = "otlp_exporter" DefaultFeatureFlag = "default" + UTRLoggingFlagTriplet = "otel_logging,otlp_exporter,otlp_logging" ) // setExperimentalFeatures sets the EXPERIMENTAL_FEATURES environment variable. @@ -1264,7 +1265,7 @@ func setExperimentalOtelLoggingInConfig(config string) string { // SetupOpsAgentWithFeatureFlag configures the VM and the config depending on the selected feature flag. func SetupOpsAgentWithFeatureFlag(ctx context.Context, logger *log.Logger, vm *gce.VM, config string, feature string) error { switch feature { - case OtelLoggingFeatureFlag: + case OtelLoggingFeatureFlag, UTRLoggingFlagTriplet: // Set feature flag in config. if config == "" { config = defaultOtelLoggingConfig()