Repository: elastic/apm-agent-python

Issue:
[META 773] Service environment correlation
See meta issue for the description and details:
- Meta issue: https://github.com/elastic/apm/issues/773



Discussion:
This will also need changes in `ecs-logging-python`.

Resolving patch:
diff --git a/elasticapm/handlers/logging.py b/elasticapm/handlers/logging.py
--- a/elasticapm/handlers/logging.py
+++ b/elasticapm/handlers/logging.py
@@ -232,11 +232,15 @@ def _add_attributes_to_log_record(record):
     service_name = client.config.service_name if client else None
     record.elasticapm_service_name = service_name
 
+    service_environment = client.config.environment if client else None
+    record.elasticapm_service_environment = service_environment
+
     record.elasticapm_labels = {
         "transaction.id": transaction_id,
         "trace.id": trace_id,
         "span.id": span_id,
         "service.name": service_name,
+        "service.environment": service_environment,
     }
 
     return record
@@ -273,6 +277,7 @@ def format(self, record):
             record.elasticapm_trace_id = None
             record.elasticapm_span_id = None
             record.elasticapm_service_name = None
+            record.elasticapm_service_environment = None
         return super(Formatter, self).format(record=record)
 
     def formatTime(self, record, datefmt=None):
@@ -281,4 +286,5 @@ def formatTime(self, record, datefmt=None):
             record.elasticapm_trace_id = None
             record.elasticapm_span_id = None
             record.elasticapm_service_name = None
+            record.elasticapm_service_environment = None
         return super(Formatter, self).formatTime(record=record, datefmt=datefmt)
diff --git a/elasticapm/handlers/structlog.py b/elasticapm/handlers/structlog.py
--- a/elasticapm/handlers/structlog.py
+++ b/elasticapm/handlers/structlog.py
@@ -57,6 +57,7 @@ def structlog_processor(logger, method_name, event_dict):
     client = get_client()
     if client:
         event_dict["service.name"] = client.config.service_name
+        event_dict["service.environment"] = client.config.environment
     if transaction and transaction.trace_parent:
         event_dict["trace.id"] = transaction.trace_parent.trace_id
     span = execution_context.get_span()


Test patch:
diff --git a/tests/handlers/logging/logging_tests.py b/tests/handlers/logging/logging_tests.py
--- a/tests/handlers/logging/logging_tests.py
+++ b/tests/handlers/logging/logging_tests.py
@@ -266,6 +266,7 @@ def test_logging_filter_no_span(elasticapm_client):
     f.filter(record)
     assert record.elasticapm_transaction_id == transaction.id
     assert record.elasticapm_service_name == transaction.tracer.config.service_name
+    assert record.elasticapm_service_environment == transaction.tracer.config.environment
     assert record.elasticapm_trace_id == transaction.trace_parent.trace_id
     assert record.elasticapm_span_id is None
     assert record.elasticapm_labels
@@ -289,6 +290,7 @@ def test_logging_filter_span(elasticapm_client):
         f.filter(record)
         assert record.elasticapm_transaction_id == transaction.id
         assert record.elasticapm_service_name == transaction.tracer.config.service_name
+        assert record.elasticapm_service_environment == transaction.tracer.config.environment
         assert record.elasticapm_trace_id == transaction.trace_parent.trace_id
         assert record.elasticapm_span_id == span.id
         assert record.elasticapm_labels
@@ -304,6 +306,7 @@ def test_logging_filter_span(elasticapm_client):
         f.filter(record)
         assert record.elasticapm_transaction_id == transaction.id
         assert record.elasticapm_service_name == transaction.tracer.config.service_name
+        assert record.elasticapm_service_environment == transaction.tracer.config.environment
         assert record.elasticapm_trace_id == transaction.trace_parent.trace_id
         assert record.elasticapm_span_id is None
         assert record.elasticapm_labels
@@ -317,6 +320,7 @@ def test_structlog_processor_span(elasticapm_client):
         new_dict = structlog_processor(None, None, event_dict)
         assert new_dict["transaction.id"] == transaction.id
         assert new_dict["service.name"] == transaction.tracer.config.service_name
+        assert new_dict["service.environment"] == transaction.tracer.config.environment
         assert new_dict["trace.id"] == transaction.trace_parent.trace_id
         assert new_dict["span.id"] == span.id
 
@@ -331,6 +335,7 @@ def test_structlog_processor_span(elasticapm_client):
         new_dict = structlog_processor(None, None, event_dict)
         assert new_dict["transaction.id"] == transaction.id
         assert new_dict["service.name"] == transaction.tracer.config.service_name
+        assert new_dict["service.environment"] == transaction.tracer.config.environment
         assert new_dict["trace.id"] == transaction.trace_parent.trace_id
         assert "span.id" not in new_dict
 
@@ -346,6 +351,7 @@ def test_automatic_log_record_factory_install(elasticapm_client):
         record = record_factory(__name__, logging.DEBUG, __file__, 252, "dummy_msg", [], None)
         assert record.elasticapm_transaction_id == transaction.id
         assert record.elasticapm_service_name == transaction.tracer.config.service_name
+        assert record.elasticapm_service_environment == transaction.tracer.config.environment
         assert record.elasticapm_trace_id == transaction.trace_parent.trace_id
         assert record.elasticapm_span_id == span.id
         assert record.elasticapm_labels
@@ -358,11 +364,13 @@ def test_formatter():
     assert "| elasticapm" in formatted_record
     assert hasattr(record, "elasticapm_transaction_id")
     assert hasattr(record, "elasticapm_service_name")
+    assert hasattr(record, "elasticapm_service_environment")
     record = logging.LogRecord(__name__, logging.DEBUG, __file__, 252, "dummy_msg", [], None)
     formatted_time = formatter.formatTime(record)
     assert formatted_time
     assert hasattr(record, "elasticapm_transaction_id")
     assert hasattr(record, "elasticapm_service_name")
+    assert hasattr(record, "elasticapm_service_environment")
 
 
 def test_logging_handler_no_client(recwarn):