From da7bc575ec26a3bc29469f75d7ca9b5c9066a0bf Mon Sep 17 00:00:00 2001
From: Steve Wilkerson <wilkers.steve@gmail.com>
Date: Sun, 17 Jun 2018 16:12:25 -0500
Subject: [PATCH] Add logging.conf files to enabled loggers/handlers/formatters

This introduces a mechanism for generating the logging.conf
file for the openstack services via the values. This allows us to
define loggers, handlers, and formatters for the services and the
modules they're composed of.

This also allows us to take advantage of the oslo fluent handler
and formatter. The fluent handler and formatter give us the
following benefits: sending logs directly to fluentd instead of
routed to stdout/stderr and then through fluentbit to fluentd,
project specific tags on the logged events (enables us to define
more robust filters in fluentd for aggregation if required),
full traceback support, and additional metadata (modules that
created logged event, etc)

Depends-On: https://review.openstack.org/577796

Change-Id: I63340ce6b03191d93a74d9ac6947f0b49b8a1a39
---
 barbican/templates/configmap-etc.yaml         | 14 ++++
 barbican/templates/deployment-api.yaml        |  4 +
 barbican/templates/job-bootstrap.yaml         |  2 +-
 barbican/templates/job-db-drop.yaml           |  2 +-
 barbican/templates/job-db-init.yaml           |  2 +-
 barbican/values.yaml                          | 75 ++++++++++++++++++
 cinder/templates/configmap-etc.yaml           | 15 ++++
 .../cron-job-cinder-volume-usage-audit.yaml   |  4 +
 cinder/templates/deployment-api.yaml          |  4 +
 cinder/templates/deployment-backup.yaml       |  4 +
 cinder/templates/deployment-scheduler.yaml    |  4 +
 cinder/templates/deployment-volume.yaml       |  4 +
 cinder/templates/job-bootstrap.yaml           |  2 +-
 cinder/values.yaml                            | 75 ++++++++++++++++++
 congress/templates/configmap-etc.yaml         | 14 ++++
 congress/templates/deployment-api.yaml        |  4 +
 congress/templates/deployment-datasource.yaml |  4 +
 .../templates/deployment-policy-engine.yaml   |  4 +
 congress/templates/job-bootstrap.yaml         |  2 +-
 congress/values.yaml                          | 75 ++++++++++++++++++
 glance/templates/configmap-etc.yaml           | 14 ++++
 glance/templates/deployment-api.yaml          |  4 +
 glance/templates/deployment-registry.yaml     |  4 +
 glance/templates/job-bootstrap.yaml           |  2 +-
 glance/templates/job-db-drop.yaml             |  2 +-
 glance/templates/job-db-init.yaml             |  2 +-
 glance/templates/job-db-sync.yaml             |  2 +-
 glance/values.yaml                            | 75 ++++++++++++++++++
 heat/templates/configmap-etc.yaml             | 14 ++++
 heat/templates/cron-job-engine-cleaner.yaml   |  4 +
 heat/templates/deployment-api.yaml            |  4 +
 heat/templates/deployment-cfn.yaml            |  4 +
 heat/templates/deployment-cloudwatch.yaml     |  4 +
 heat/templates/deployment-engine.yaml         |  4 +
 heat/templates/job-bootstrap.yaml             |  3 +-
 heat/values.yaml                              | 75 ++++++++++++++++++
 ironic/templates/configmap-etc.yaml           | 14 ++++
 ironic/templates/deployment-api.yaml          |  4 +
 ironic/templates/job-bootstrap.yaml           |  2 +-
 ironic/templates/statefulset-conductor.yaml   |  4 +
 ironic/values.yaml                            | 75 ++++++++++++++++++
 keystone/templates/configmap-etc.yaml         | 14 ++++
 .../templates/cron-job-credential-rotate.yaml |  4 +
 .../templates/cron-job-fernet-rotate.yaml     |  4 +
 keystone/templates/deployment-api.yaml        |  4 +
 keystone/templates/job-bootstrap.yaml         |  2 +-
 keystone/templates/job-credential-setup.yaml  |  4 +
 keystone/templates/job-domain-manage.yaml     |  4 +
 keystone/templates/job-fernet-setup.yaml      |  4 +
 keystone/values.yaml                          | 75 ++++++++++++++++++
 magnum/templates/configmap-etc.yaml           | 14 ++++
 magnum/templates/deployment-api.yaml          |  4 +
 magnum/templates/job-bootstrap.yaml           |  2 +-
 magnum/templates/statefulset-conductor.yaml   |  4 +
 magnum/values.yaml                            | 75 ++++++++++++++++++
 mistral/templates/configmap-etc.yaml          | 14 ++++
 mistral/templates/deployment-api.yaml         |  4 +
 mistral/templates/deployment-executor.yaml    |  4 +
 mistral/templates/job-bootstrap.yaml          |  2 +-
 mistral/templates/statefulset-engine.yaml     |  4 +
 .../templates/statefulset-event-engine.yaml   |  4 +
 mistral/values.yaml                           | 75 ++++++++++++++++++
 neutron/templates/configmap-etc.yaml          | 14 ++++
 neutron/templates/daemonset-dhcp-agent.yaml   |  4 +
 neutron/templates/daemonset-l3-agent.yaml     |  4 +
 neutron/templates/daemonset-lb-agent.yaml     |  4 +
 .../templates/daemonset-metadata-agent.yaml   |  4 +
 neutron/templates/daemonset-ovs-agent.yaml    |  4 +
 neutron/templates/daemonset-sriov-agent.yaml  |  4 +
 neutron/templates/deployment-server.yaml      |  4 +
 neutron/templates/job-bootstrap.yaml          |  2 +-
 neutron/values.yaml                           | 75 ++++++++++++++++++
 nova/templates/configmap-etc.yaml             | 14 ++++
 nova/templates/cron-job-cell-setup.yaml       |  4 +
 nova/templates/daemonset-compute.yaml         |  4 +
 nova/templates/deployment-api-metadata.yaml   |  4 +
 nova/templates/deployment-api-osapi.yaml      |  4 +
 nova/templates/deployment-conductor.yaml      |  4 +
 nova/templates/deployment-consoleauth.yaml    |  4 +
 nova/templates/deployment-novncproxy.yaml     |  8 ++
 nova/templates/deployment-placement.yaml      |  4 +
 nova/templates/deployment-scheduler.yaml      |  4 +
 nova/templates/deployment-spiceproxy.yaml     |  8 ++
 nova/templates/job-bootstrap.yaml             |  2 +-
 nova/templates/job-cell-setup.yaml            |  4 +
 nova/templates/job-db-drop.yaml               |  6 +-
 nova/templates/job-db-init.yaml               |  6 +-
 .../templates/statefulset-compute-ironic.yaml |  4 +
 nova/values.yaml                              | 75 ++++++++++++++++++
 senlin/templates/configmap-etc.yaml           | 14 ++++
 senlin/templates/cron-job-engine-cleaner.yaml |  4 +
 senlin/templates/deployment-api.yaml          |  4 +
 senlin/templates/deployment-engine.yaml       |  4 +
 senlin/templates/job-bootstrap.yaml           |  2 +-
 senlin/values.yaml                            | 76 ++++++++++++++++++-
 95 files changed, 1309 insertions(+), 24 deletions(-)

diff --git a/barbican/templates/configmap-etc.yaml b/barbican/templates/configmap-etc.yaml
index f592981795..791404ca74 100644
--- a/barbican/templates/configmap-etc.yaml
+++ b/barbican/templates/configmap-etc.yaml
@@ -72,6 +72,18 @@ limitations under the License.
 {{- $_ := printf ":%s" ( tuple "key_manager" "internal" "api" . | include "helm-toolkit.endpoints.endpoint_port_lookup" ) | set .Values.conf.barbican_api.uwsgi "socket" -}}
 {{- end -}}
 
+{{- if and (empty .Values.conf.logging.handler_fluent) (has "fluent" .Values.conf.logging.handlers.keys) -}}
+{{- $fluentd_host := tuple "fluentd" "internal" $envAll | include "helm-toolkit.endpoints.hostname_namespaced_endpoint_lookup" }}
+{{- $fluentd_port := tuple "fluentd" "internal" "service" $envAll | include "helm-toolkit.endpoints.endpoint_port_lookup" }}
+{{- $fluent_args := printf "('openstack.barbican', '%s', %s)" $fluentd_host $fluentd_port }}
+{{- $handler_fluent := dict "class" "fluent.handler.FluentHandler" "formatter" "fluent" "args" $fluent_args -}}
+{{- $_ := set .Values.conf.logging "handler_fluent" $handler_fluent -}}
+{{- end -}}
+
+{{- if and (empty .Values.conf.logging.formatter_fluent) (has "fluent" .Values.conf.logging.formatters.keys) -}}
+{{- $formatter_fluent := dict "class" "oslo_log.formatters.FluentFormatter" -}}
+{{- $_ := set .Values.conf.logging "formatter_fluent" $formatter_fluent -}}
+{{- end -}}
 ---
 apiVersion: v1
 kind: ConfigMap
@@ -80,6 +92,8 @@ metadata:
 data:
   barbican.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.barbican | indent 4 }}
+  logging.conf: |
+{{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.logging | indent 4 }}
   barbican-api-paste.ini: |
 {{ include "helm-toolkit.utils.to_ini" .Values.conf.paste | indent 4 }}
   api_audit_map.conf: |
diff --git a/barbican/templates/deployment-api.yaml b/barbican/templates/deployment-api.yaml
index 7b309a885d..5b36193b54 100644
--- a/barbican/templates/deployment-api.yaml
+++ b/barbican/templates/deployment-api.yaml
@@ -82,6 +82,10 @@ spec:
               mountPath: /etc/barbican/barbican.conf
               subPath: barbican.conf
               readOnly: true
+            - name: barbican-etc
+              mountPath: {{ .Values.conf.barbican.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.barbican.DEFAULT.log_config_append }}
+              readOnly: true
             - name: barbican-etc
               mountPath: /etc/barbican/api_audit_map.conf
               subPath: api_audit_map.conf
diff --git a/barbican/templates/job-bootstrap.yaml b/barbican/templates/job-bootstrap.yaml
index ba1d10ab9e..df823474b4 100644
--- a/barbican/templates/job-bootstrap.yaml
+++ b/barbican/templates/job-bootstrap.yaml
@@ -15,6 +15,6 @@ limitations under the License.
 */}}
 
 {{- if and .Values.manifests.job_bootstrap .Values.bootstrap.enabled }}
-{{- $bootstrapJob := dict "envAll" . "serviceName" "barbican" "keystoneUser" .Values.bootstrap.ks_user -}}
+{{- $bootstrapJob := dict "envAll" . "serviceName" "barbican" "keystoneUser" .Values.bootstrap.ks_user "logConfigFile" .Values.conf.barbican.DEFAULT.log_config_append -}}
 {{ $bootstrapJob | include "helm-toolkit.manifests.job_bootstrap" }}
 {{- end }}
diff --git a/barbican/templates/job-db-drop.yaml b/barbican/templates/job-db-drop.yaml
index 3c6ec5d451..0395fa09be 100644
--- a/barbican/templates/job-db-drop.yaml
+++ b/barbican/templates/job-db-drop.yaml
@@ -16,7 +16,7 @@ limitations under the License.
 
 {{- if .Values.manifests.job_db_drop }}
 {{- $serviceName := "barbican" -}}
-{{- $dbToDrop := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "configDbSection" "DEFAULT" "configDbKey" "sql_connection" -}}
+{{- $dbToDrop := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "logConfigFile" (printf "/etc/%s/logging.conf" $serviceName ) "configDbSection" "DEFAULT" "configDbKey" "sql_connection" -}}
 {{- $dbDropJob := dict "envAll" . "serviceName" $serviceName "dbToDrop" $dbToDrop -}}
 {{ $dbDropJob | include "helm-toolkit.manifests.job_db_drop_mysql" }}
 {{- end }}
diff --git a/barbican/templates/job-db-init.yaml b/barbican/templates/job-db-init.yaml
index c2ba7c7bff..e3b0dd4a68 100644
--- a/barbican/templates/job-db-init.yaml
+++ b/barbican/templates/job-db-init.yaml
@@ -16,7 +16,7 @@ limitations under the License.
 
 {{- if .Values.manifests.job_db_init }}
 {{- $serviceName := "barbican" -}}
-{{- $dbToInit := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "configDbSection" "DEFAULT" "configDbKey" "sql_connection" -}}
+{{- $dbToInit := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "logConfigFile" (printf "/etc/%s/logging.conf" $serviceName ) "configDbSection" "DEFAULT" "configDbKey" "sql_connection" -}}
 {{- $dbInitJob := dict "envAll" . "serviceName" $serviceName "dbToInit" $dbToInit -}}
 {{ $dbInitJob | include "helm-toolkit.manifests.job_db_init_mysql" }}
 {{- end }}
diff --git a/barbican/values.yaml b/barbican/values.yaml
index b7a803975a..d30a72dd80 100644
--- a/barbican/values.yaml
+++ b/barbican/values.yaml
@@ -415,6 +415,7 @@ conf:
   barbican:
     DEFAULT:
       transport_url: null
+      log_config_append: /etc/barbican/logging.conf
     keystone_authtoken:
       auth_type: password
       auth_version: v3
@@ -426,6 +427,65 @@ conf:
       #NOTE(portdirect): the bind port should not be defined, and is manipulated
       # via the endpoints section.
       bind_port: null
+  logging:
+    loggers:
+      keys:
+        - root
+        - barbican
+    handlers:
+      keys:
+        - stdout
+        - stderr
+        - "null"
+    formatters:
+      keys:
+        - context
+        - default
+    logger_root:
+      level: WARNING
+      handlers: null
+    logger_barbican:
+      level: INFO
+      handlers:
+        - stdout
+        - stderr
+      qualname: barbican
+    logger_amqp:
+      level: WARNING
+      handlers: stderr
+      qualname: amqp
+    logger_amqplib:
+      level: WARNING
+      handlers: stderr
+      qualname: amqplib
+    logger_eventletwsgi:
+      level: WARNING
+      handlers: stderr
+      qualname: eventlet.wsgi.server
+    logger_sqlalchemy:
+      level: WARNING
+      handlers: stderr
+      qualname: sqlalchemy
+    logger_boto:
+      level: WARNING
+      handlers: stderr
+      qualname: boto
+    handler_null:
+      class: logging.NullHandler
+      formatter: default
+      args: ()
+    handler_stdout:
+      class: StreamHandler
+      args: (sys.stdout,)
+      formatter: context
+    handler_stderr:
+      class: StreamHandler
+      args: (sys.stderr,)
+      formatter: context
+    formatter_context:
+      class: oslo_log.formatters.ContextFormatter
+    formatter_default:
+      format: "%(message)s"
 
 # Names of secrets used by bootstrap and environmental checks
 secrets:
@@ -551,6 +611,21 @@ endpoints:
     port:
       memcache:
         default: 11211
+  fluentd:
+    namespace: null
+    name: fluentd
+    hosts:
+      default: fluentd-logging
+    host_fqdn_override:
+      default: null
+    path:
+      default: null
+    scheme: 'http'
+    port:
+      service:
+        default: 24224
+      metrics:
+        default: 24220
 
 manifests:
   configmap_bin: true
diff --git a/cinder/templates/configmap-etc.yaml b/cinder/templates/configmap-etc.yaml
index d00dcd4558..fc82047c52 100644
--- a/cinder/templates/configmap-etc.yaml
+++ b/cinder/templates/configmap-etc.yaml
@@ -93,6 +93,19 @@ limitations under the License.
 {{- if empty .Values.conf.cinder.DEFAULT.osapi_volume_listen_port -}}
 {{- $_ := tuple "volume" "internal" "api" . | include "helm-toolkit.endpoints.endpoint_port_lookup" | set .Values.conf.cinder.DEFAULT "osapi_volume_listen_port" -}}
 {{- end -}}
+
+{{- if and (empty .Values.conf.logging.handler_fluent) (has "fluent" .Values.conf.logging.handlers.keys) -}}
+{{- $fluentd_host := tuple "fluentd" "internal" $envAll | include "helm-toolkit.endpoints.hostname_namespaced_endpoint_lookup" }}
+{{- $fluentd_port := tuple "fluentd" "internal" "service" $envAll | include "helm-toolkit.endpoints.endpoint_port_lookup" }}
+{{- $fluent_args := printf "('openstack.cinder', '%s', %s)" $fluentd_host $fluentd_port }}
+{{- $handler_fluent := dict "class" "fluent.handler.FluentHandler" "formatter" "fluent" "args" $fluent_args -}}
+{{- $_ := set .Values.conf.logging "handler_fluent" $handler_fluent -}}
+{{- end -}}
+
+{{- if and (empty .Values.conf.logging.formatter_fluent) (has "fluent" .Values.conf.logging.formatters.keys) -}}
+{{- $formatter_fluent := dict "class" "oslo_log.formatters.FluentFormatter" -}}
+{{- $_ := set .Values.conf.logging "formatter_fluent" $formatter_fluent -}}
+{{- end -}}
 ---
 apiVersion: v1
 kind: ConfigMap
@@ -103,6 +116,8 @@ data:
 {{ toYaml .Values.conf.rally_tests.tests | indent 4 }}
   cinder.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.cinder | indent 4 }}
+  logging.conf: |
+{{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.logging | indent 4 }}
   backends.conf: |
 {{ include "helm-toolkit.utils.to_ini" .Values.conf.backends | indent 4 }}
   api-paste.ini: |
diff --git a/cinder/templates/cron-job-cinder-volume-usage-audit.yaml b/cinder/templates/cron-job-cinder-volume-usage-audit.yaml
index 3e5fa88b5a..99359ad8e1 100644
--- a/cinder/templates/cron-job-cinder-volume-usage-audit.yaml
+++ b/cinder/templates/cron-job-cinder-volume-usage-audit.yaml
@@ -58,6 +58,10 @@ spec:
                 mountPath: /etc/cinder/cinder.conf
                 subPath: cinder.conf
                 readOnly: true
+              - name: cinder-etc
+                mountPath: {{ .Values.conf.cinder.DEFAULT.log_config_append }}
+                subPath: {{ base .Values.conf.cinder.DEFAULT.log_config_append }}
+                readOnly: true
               - name: cinder-bin
                 mountPath: /tmp/volume-usage-audit.sh
                 subPath: volume-usage-audit.sh
diff --git a/cinder/templates/deployment-api.yaml b/cinder/templates/deployment-api.yaml
index 17a8206d72..b84e46e4ef 100644
--- a/cinder/templates/deployment-api.yaml
+++ b/cinder/templates/deployment-api.yaml
@@ -95,6 +95,10 @@ spec:
               mountPath: /etc/cinder/cinder.conf
               subPath: cinder.conf
               readOnly: true
+            - name: cinder-etc
+              mountPath: {{ .Values.conf.cinder.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.cinder.DEFAULT.log_config_append }}
+              readOnly: true
             - name: cinder-etc
               mountPath: /etc/cinder/api-paste.ini
               subPath: api-paste.ini
diff --git a/cinder/templates/deployment-backup.yaml b/cinder/templates/deployment-backup.yaml
index bfbd36a440..94d1ff0442 100644
--- a/cinder/templates/deployment-backup.yaml
+++ b/cinder/templates/deployment-backup.yaml
@@ -142,6 +142,10 @@ spec:
               mountPath: /etc/cinder/cinder.conf
               subPath: cinder.conf
               readOnly: true
+            - name: cinder-etc
+              mountPath: {{ .Values.conf.cinder.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.cinder.DEFAULT.log_config_append }}
+              readOnly: true
             {{ if or (eq .Values.conf.cinder.DEFAULT.backup_driver "cinder.backup.drivers.ceph") (include "cinder.utils.is_ceph_volume_configured" $envAll) }}
             - name: etcceph
               mountPath: /etc/ceph
diff --git a/cinder/templates/deployment-scheduler.yaml b/cinder/templates/deployment-scheduler.yaml
index 3ad901db0a..cef6a5eac5 100644
--- a/cinder/templates/deployment-scheduler.yaml
+++ b/cinder/templates/deployment-scheduler.yaml
@@ -81,6 +81,10 @@ spec:
               mountPath: /etc/cinder/cinder.conf
               subPath: cinder.conf
               readOnly: true
+            - name: cinder-etc
+              mountPath: {{ .Values.conf.cinder.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.cinder.DEFAULT.log_config_append }}
+              readOnly: true
             - name: cinder-etc
               mountPath: /etc/cinder/api-paste.ini
               subPath: api-paste.ini
diff --git a/cinder/templates/deployment-volume.yaml b/cinder/templates/deployment-volume.yaml
index ca7d6fb9b5..64b71f0053 100644
--- a/cinder/templates/deployment-volume.yaml
+++ b/cinder/templates/deployment-volume.yaml
@@ -103,6 +103,10 @@ spec:
               mountPath: /etc/cinder/cinder.conf
               subPath: cinder.conf
               readOnly: true
+            - name: cinder-etc
+              mountPath: {{ .Values.conf.cinder.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.cinder.DEFAULT.log_config_append }}
+              readOnly: true
             - name: cinder-etc
               mountPath: /etc/cinder/conf/backends.conf
               subPath: backends.conf
diff --git a/cinder/templates/job-bootstrap.yaml b/cinder/templates/job-bootstrap.yaml
index 4b5d048b66..63192f6fcd 100644
--- a/cinder/templates/job-bootstrap.yaml
+++ b/cinder/templates/job-bootstrap.yaml
@@ -15,6 +15,6 @@ limitations under the License.
 */}}
 
 {{- if and .Values.manifests.job_bootstrap .Values.bootstrap.enabled }}
-{{- $bootstrapJob := dict "envAll" . "serviceName" "cinder" "keystoneUser" .Values.bootstrap.ks_user -}}
+{{- $bootstrapJob := dict "envAll" . "serviceName" "cinder" "keystoneUser" .Values.bootstrap.ks_user "logConfigFile" .Values.conf.cinder.DEFAULT.log_config_append -}}
 {{ $bootstrapJob | include "helm-toolkit.manifests.job_bootstrap" }}
 {{- end }}
diff --git a/cinder/values.yaml b/cinder/values.yaml
index b61e0fa8fa..fcd520e19c 100644
--- a/cinder/values.yaml
+++ b/cinder/values.yaml
@@ -716,6 +716,7 @@ conf:
         chunk_size: 8
   cinder:
     DEFAULT:
+      log_config_append: /etc/cinder/logging.conf
       use_syslog: false
       use_stderr: true
       enable_v1_api: false
@@ -749,6 +750,65 @@ conf:
       driver: messagingv2
     coordination:
       backend_url: file:///var/lib/cinder/coordination
+  logging:
+    loggers:
+      keys:
+        - root
+        - cinder
+    handlers:
+      keys:
+        - stdout
+        - stderr
+        - "null"
+    formatters:
+      keys:
+        - context
+        - default
+    logger_root:
+      level: WARNING
+      handlers: null
+    logger_cinder:
+      level: INFO
+      handlers:
+        - stdout
+        - stderr
+      qualname: cinder
+    logger_amqp:
+      level: WARNING
+      handlers: stderr
+      qualname: amqp
+    logger_amqplib:
+      level: WARNING
+      handlers: stderr
+      qualname: amqplib
+    logger_eventletwsgi:
+      level: WARNING
+      handlers: stderr
+      qualname: eventlet.wsgi.server
+    logger_sqlalchemy:
+      level: WARNING
+      handlers: stderr
+      qualname: sqlalchemy
+    logger_boto:
+      level: WARNING
+      handlers: stderr
+      qualname: boto
+    handler_null:
+      class: logging.NullHandler
+      formatter: default
+      args: ()
+    handler_stdout:
+      class: StreamHandler
+      args: (sys.stdout,)
+      formatter: context
+    handler_stderr:
+      class: StreamHandler
+      args: (sys.stderr,)
+      formatter: context
+    formatter_context:
+      class: oslo_log.formatters.ContextFormatter
+    formatter_default:
+      format: "%(message)s"
   backends:
     # Those options will be written to backends.conf as-is.
     rbd1:
@@ -1118,6 +1178,21 @@ endpoints:
     port:
       memcache:
         default: 11211
+  fluentd:
+    namespace: null
+    name: fluentd
+    hosts:
+      default: fluentd-logging
+    host_fqdn_override:
+      default: null
+    path:
+      default: null
+    scheme: 'http'
+    port:
+      service:
+        default: 24224
+      metrics:
+        default: 24220
 
 manifests:
   configmap_bin: true
diff --git a/congress/templates/configmap-etc.yaml b/congress/templates/configmap-etc.yaml
index 13aea64571..67449f2c52 100644
--- a/congress/templates/configmap-etc.yaml
+++ b/congress/templates/configmap-etc.yaml
@@ -63,6 +63,18 @@ limitations under the License.
 {{- $_ := tuple "policy" "internal" "api" . | include "helm-toolkit.endpoints.endpoint_port_lookup" | set .Values.conf.congress.DEFAULT "bind_port" -}}
 {{- end -}}
 
+{{- if and (empty .Values.conf.logging.handler_fluent) (has "fluent" .Values.conf.logging.handlers.keys) -}}
+{{- $fluentd_host := tuple "fluentd" "internal" $envAll | include "helm-toolkit.endpoints.hostname_namespaced_endpoint_lookup" }}
+{{- $fluentd_port := tuple "fluentd" "internal" "service" $envAll | include "helm-toolkit.endpoints.endpoint_port_lookup" }}
+{{- $fluent_args := printf "('openstack.congress', '%s', %s)" $fluentd_host $fluentd_port }}
+{{- $handler_fluent := dict "class" "fluent.handler.FluentHandler" "formatter" "fluent" "args" $fluent_args -}}
+{{- $_ := set .Values.conf.logging "handler_fluent" $handler_fluent -}}
+{{- end -}}
+
+{{- if and (empty .Values.conf.logging.formatter_fluent) (has "fluent" .Values.conf.logging.formatters.keys) -}}
+{{- $formatter_fluent := dict "class" "oslo_log.formatters.FluentFormatter" -}}
+{{- $_ := set .Values.conf.logging "formatter_fluent" $formatter_fluent -}}
+{{- end -}}
 ---
 apiVersion: v1
 kind: ConfigMap
@@ -71,6 +83,8 @@ metadata:
 data:
   congress.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.congress | indent 4 }}
+  logging.conf: |
+{{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.logging | indent 4 }}
   api-paste.ini: |
 {{ include "helm-toolkit.utils.to_ini" .Values.conf.paste | indent 4 }}
   policy.json: |
diff --git a/congress/templates/deployment-api.yaml b/congress/templates/deployment-api.yaml
index 97ba824055..f2e6ba7a20 100644
--- a/congress/templates/deployment-api.yaml
+++ b/congress/templates/deployment-api.yaml
@@ -69,6 +69,10 @@ spec:
               mountPath: /etc/congress/congress.conf
               subPath: congress.conf
               readOnly: true
+            - name: congress-etc
+              mountPath: {{ .Values.conf.congress.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.congress.DEFAULT.log_config_append }}
+              readOnly: true
             - name: congress-etc
               mountPath: /etc/congress/api-paste.ini
               subPath: api-paste.ini
diff --git a/congress/templates/deployment-datasource.yaml b/congress/templates/deployment-datasource.yaml
index 0d58fde11f..9047d50df3 100644
--- a/congress/templates/deployment-datasource.yaml
+++ b/congress/templates/deployment-datasource.yaml
@@ -63,6 +63,10 @@ spec:
               mountPath: /etc/congress/congress.conf
               subPath: congress.conf
               readOnly: true
+            - name: congress-etc
+              mountPath: {{ .Values.conf.congress.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.congress.DEFAULT.log_config_append }}
+              readOnly: true
             - name: congress-etc
               mountPath: /etc/congress/api-paste.ini
               subPath: api-paste.ini
diff --git a/congress/templates/deployment-policy-engine.yaml b/congress/templates/deployment-policy-engine.yaml
index 41b7fe6614..452f6dc97c 100644
--- a/congress/templates/deployment-policy-engine.yaml
+++ b/congress/templates/deployment-policy-engine.yaml
@@ -63,6 +63,10 @@ spec:
               mountPath: /etc/congress/congress.conf
               subPath: congress.conf
               readOnly: true
+            - name: congress-etc
+              mountPath: {{ .Values.conf.congress.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.congress.DEFAULT.log_config_append }}
+              readOnly: true
             - name: congress-etc
               mountPath: /etc/congress/api-paste.ini
               subPath: api-paste.ini
diff --git a/congress/templates/job-bootstrap.yaml b/congress/templates/job-bootstrap.yaml
index 88b80cbaa7..703131d76f 100644
--- a/congress/templates/job-bootstrap.yaml
+++ b/congress/templates/job-bootstrap.yaml
@@ -15,6 +15,6 @@ limitations under the License.
 */}}
 
 {{- if and .Values.manifests.job_bootstrap .Values.bootstrap.enabled }}
-{{- $bootstrapJob := dict "envAll" . "serviceName" "congress" "keystoneUser" .Values.bootstrap.ks_user -}}
+{{- $bootstrapJob := dict "envAll" . "serviceName" "congress" "keystoneUser" .Values.bootstrap.ks_user "logConfigFile" .Values.conf.congress.DEFAULT.log_config_append -}}
 {{ $bootstrapJob | include "helm-toolkit.manifests.job_bootstrap" }}
 {{- end }}
diff --git a/congress/values.yaml b/congress/values.yaml
index fb80d3f509..1f9ca94db1 100644
--- a/congress/values.yaml
+++ b/congress/values.yaml
@@ -315,6 +315,21 @@ endpoints:
         default: 5672
       http:
         default: 15672
+  fluentd:
+    namespace: null
+    name: fluentd
+    hosts:
+      default: fluentd-logging
+    host_fqdn_override:
+      default: null
+    path:
+      default: null
+    scheme: 'http'
+    port:
+      service:
+        default: 24224
+      metrics:
+        default: 24220
 
 policy:
   datasource_services:
@@ -329,6 +344,7 @@ policy:
 conf:
   congress:
     DEFAULT:
+      log_config_append: /etc/congress/logging.conf
       #NOTE(portdirect): the bind port should not be defined, and is manipulated
       # via the endpoints section.
       bind_port: null
@@ -347,6 +363,65 @@ conf:
       max_retries: -1
     keystone_authtoken:
       auth_type: password
+  logging:
+    loggers:
+      keys:
+        - root
+        - congress
+    handlers:
+      keys:
+        - stdout
+        - stderr
+        - "null"
+    formatters:
+      keys:
+        - context
+        - default
+    logger_root:
+      level: WARNING
+      handlers: null
+    logger_congress:
+      level: INFO
+      handlers:
+        - stdout
+        - stderr
+      qualname: congress
+    logger_amqp:
+      level: WARNING
+      handlers: stderr
+      qualname: amqp
+    logger_amqplib:
+      level: WARNING
+      handlers: stderr
+      qualname: amqplib
+    logger_eventletwsgi:
+      level: WARNING
+      handlers: stderr
+      qualname: eventlet.wsgi.server
+    logger_sqlalchemy:
+      level: WARNING
+      handlers: stderr
+      qualname: sqlalchemy
+    logger_boto:
+      level: WARNING
+      handlers: stderr
+      qualname: boto
+    handler_null:
+      class: logging.NullHandler
+      formatter: default
+      args: ()
+    handler_stdout:
+      class: StreamHandler
+      args: (sys.stdout,)
+      formatter: context
+    handler_stderr:
+      class: StreamHandler
+      args: (sys.stderr,)
+      formatter: context
+    formatter_context:
+      class: oslo_log.formatters.ContextFormatter
+    formatter_default:
+      format: "%(message)s"
   paste:
     composite:congress:
       use: egg:Paste#urlmap
diff --git a/glance/templates/configmap-etc.yaml b/glance/templates/configmap-etc.yaml
index 0070ed86d4..a2541e91cc 100644
--- a/glance/templates/configmap-etc.yaml
+++ b/glance/templates/configmap-etc.yaml
@@ -144,6 +144,18 @@ limitations under the License.
 {{- $_ := tuple "image_registry" "internal" "api" . | include "helm-toolkit.endpoints.endpoint_port_lookup" | set .Values.conf.glance_registry.DEFAULT "bind_port" -}}
 {{- end -}}
 
+{{- if and (empty .Values.conf.logging.handler_fluent) (has "fluent" .Values.conf.logging.handlers.keys) -}}
+{{- $fluentd_host := tuple "fluentd" "internal" $envAll | include "helm-toolkit.endpoints.hostname_namespaced_endpoint_lookup" }}
+{{- $fluentd_port := tuple "fluentd" "internal" "service" $envAll | include "helm-toolkit.endpoints.endpoint_port_lookup" }}
+{{- $fluent_args := printf "('openstack.glance', '%s', %s)" $fluentd_host $fluentd_port }}
+{{- $handler_fluent := dict "class" "fluent.handler.FluentHandler" "formatter" "fluent" "args" $fluent_args -}}
+{{- $_ := set .Values.conf.logging "handler_fluent" $handler_fluent -}}
+{{- end -}}
+
+{{- if and (empty .Values.conf.logging.formatter_fluent) (has "fluent" .Values.conf.logging.formatters.keys) -}}
+{{- $formatter_fluent := dict "class" "oslo_log.formatters.FluentFormatter" -}}
+{{- $_ := set .Values.conf.logging "formatter_fluent" $formatter_fluent -}}
+{{- end -}}
 ---
 apiVersion: v1
 kind: ConfigMap
@@ -154,6 +166,8 @@ data:
 {{ toYaml .Values.conf.rally_tests.tests | indent 4 }}
   glance-api.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.glance | indent 4 }}
+  logging.conf: |
+{{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.logging | indent 4 }}
   glance-api-paste.ini: |
 {{ include "helm-toolkit.utils.to_ini" .Values.conf.paste | indent 4 }}
   glance-registry.conf: |
diff --git a/glance/templates/deployment-api.yaml b/glance/templates/deployment-api.yaml
index 5c9105a68a..78902ad8fe 100644
--- a/glance/templates/deployment-api.yaml
+++ b/glance/templates/deployment-api.yaml
@@ -118,6 +118,10 @@ spec:
               mountPath: /etc/glance/glance-api.conf
               subPath: glance-api.conf
               readOnly: true
+            - name: glance-etc
+              mountPath: {{ .Values.conf.glance.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.glance.DEFAULT.log_config_append }}
+              readOnly: true
             - name: glance-etc
               mountPath: /etc/glance/glance-api-paste.ini
               subPath: glance-api-paste.ini
diff --git a/glance/templates/deployment-registry.yaml b/glance/templates/deployment-registry.yaml
index a1191dc238..0852e22473 100644
--- a/glance/templates/deployment-registry.yaml
+++ b/glance/templates/deployment-registry.yaml
@@ -83,6 +83,10 @@ spec:
               mountPath: /etc/glance/glance-registry.conf
               subPath: glance-registry.conf
               readOnly: true
+            - name: glance-etc
+              mountPath: {{ .Values.conf.glance.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.glance.DEFAULT.log_config_append }}
+              readOnly: true
             - name: glance-etc
               mountPath: /etc/glance/glance-registry-paste.ini
               subPath: glance-registry-paste.ini
diff --git a/glance/templates/job-bootstrap.yaml b/glance/templates/job-bootstrap.yaml
index d26ea7849a..7578b7ec50 100644
--- a/glance/templates/job-bootstrap.yaml
+++ b/glance/templates/job-bootstrap.yaml
@@ -26,6 +26,6 @@ volumes:
 
 {{- if and .Values.manifests.job_bootstrap .Values.bootstrap.enabled }}
 {{- $podVolumes := tuple . | include "glance.templates._job_bootstrap.pod_volumes" | toString | fromYaml }}
-{{- $bootstrapJob := dict "envAll" . "serviceName" "glance" "keystoneUser" .Values.bootstrap.ks_user "podVolMounts" $podVolumes.volumeMounts "podVols" $podVolumes.volumes -}}
+{{- $bootstrapJob := dict "envAll" . "serviceName" "glance" "keystoneUser" .Values.bootstrap.ks_user "logConfigFile" .Values.conf.glance.DEFAULT.log_config_append "podVolMounts" $podVolumes.volumeMounts "podVols" $podVolumes.volumes -}}
 {{ $bootstrapJob | include "helm-toolkit.manifests.job_bootstrap" }}
 {{- end }}
diff --git a/glance/templates/job-db-drop.yaml b/glance/templates/job-db-drop.yaml
index 1622b06244..4bc07ac135 100644
--- a/glance/templates/job-db-drop.yaml
+++ b/glance/templates/job-db-drop.yaml
@@ -16,7 +16,7 @@ limitations under the License.
 
 {{- if .Values.manifests.job_db_drop }}
 {{- $serviceName := "glance" -}}
-{{- $dbToDrop := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName "glance-api" ) "configDbSection" "database" "configDbKey" "connection" -}}
+{{- $dbToDrop := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName "glance-api" ) "logConfigFile" (printf "/etc/%s/logging.conf" $serviceName ) "configDbSection" "database" "configDbKey" "connection" -}}
 {{- $dbDropJob := dict "envAll" . "serviceName" $serviceName "dbToDrop" $dbToDrop -}}
 {{ $dbDropJob | include "helm-toolkit.manifests.job_db_drop_mysql" }}
 {{- end }}
diff --git a/glance/templates/job-db-init.yaml b/glance/templates/job-db-init.yaml
index 282f626a6b..476f860bf0 100644
--- a/glance/templates/job-db-init.yaml
+++ b/glance/templates/job-db-init.yaml
@@ -16,7 +16,7 @@ limitations under the License.
 
 {{- if .Values.manifests.job_db_init }}
 {{- $serviceName := "glance" -}}
-{{- $dbToInit := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName "glance-api" ) "configDbSection" "database" "configDbKey" "connection" -}}
+{{- $dbToInit := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName "glance-api" ) "logConfigFile" (printf "/etc/%s/logging.conf" $serviceName ) "configDbSection" "database" "configDbKey" "connection" -}}
 {{- $dbInitJob := dict "envAll" . "serviceName" $serviceName "dbToInit" $dbToInit -}}
 {{ $dbInitJob | include "helm-toolkit.manifests.job_db_init_mysql" }}
 {{- end }}
diff --git a/glance/templates/job-db-sync.yaml b/glance/templates/job-db-sync.yaml
index 8c338aec45..5ee2c83ab4 100644
--- a/glance/templates/job-db-sync.yaml
+++ b/glance/templates/job-db-sync.yaml
@@ -16,7 +16,7 @@ limitations under the License.
 
 {{- if .Values.manifests.job_db_sync }}
 {{- $serviceName := "glance" -}}
-{{- $dbToSync := index . "dbToSync" | default ( dict "configFile" (printf "/etc/%s/%s.conf" $serviceName "glance-api" ) "image" ( index .Values.images.tags ( printf "%s_db_sync" $serviceName )) ) -}}
+{{- $dbToSync := index . "dbToSync" | default ( dict "configFile" (printf "/etc/%s/%s.conf" $serviceName "glance-api" ) "logConfigFile" (printf "/etc/%s/logging.conf" $serviceName ) "image" ( index .Values.images.tags ( printf "%s_db_sync" $serviceName )) ) -}}
 {{- $dbSyncJob := dict "envAll" . "serviceName" $serviceName "dbToSync" $dbToSync -}}
 {{ $dbSyncJob | include "helm-toolkit.manifests.job_db_sync" }}
 {{- end }}
diff --git a/glance/values.yaml b/glance/values.yaml
index 4ff296c79f..b03bc31757 100644
--- a/glance/values.yaml
+++ b/glance/values.yaml
@@ -221,6 +221,7 @@ conf:
     add_metadef_tags: ''
   glance:
     DEFAULT:
+      log_config_append: /etc/glance/logging.conf
       # NOTE(portdirect): the bind port should not be defined, and is manipulated
       # via the endpoints section.
       bind_port: null
@@ -246,6 +247,65 @@ conf:
       max_retries: -1
     oslo_messaging_notifications:
       driver: messagingv2
+  logging:
+    loggers:
+      keys:
+        - root
+        - glance
+    handlers:
+      keys:
+        - stdout
+        - stderr
+        - "null"
+    formatters:
+      keys:
+        - context
+        - default
+    logger_root:
+      level: WARNING
+      handlers: null
+    logger_glance:
+      level: INFO
+      handlers:
+        - stdout
+        - stderr
+      qualname: glance
+    logger_amqp:
+      level: WARNING
+      handlers: stderr
+      qualname: amqp
+    logger_amqplib:
+      level: WARNING
+      handlers: stderr
+      qualname: amqplib
+    logger_eventletwsgi:
+      level: WARNING
+      handlers: stderr
+      qualname: eventlet.wsgi.server
+    logger_sqlalchemy:
+      level: WARNING
+      handlers: stderr
+      qualname: sqlalchemy
+    logger_boto:
+      level: WARNING
+      handlers: stderr
+      qualname: boto
+    handler_null:
+      class: logging.NullHandler
+      formatter: default
+      args: ()
+    handler_stdout:
+      class: StreamHandler
+      args: (sys.stdout,)
+      formatter: context
+    handler_stderr:
+      class: StreamHandler
+      args: (sys.stderr,)
+      formatter: context
+    formatter_context:
+      class: oslo_log.formatters.ContextFormatter
+    formatter_default:
+      format: "%(message)s"
   paste_registry:
     pipeline:glance-registry:
       pipeline: healthcheck osprofiler unauthenticated-context registryapp
@@ -621,6 +681,21 @@ endpoints:
       api:
         default: 8088
         public: 80
+  fluentd:
+    namespace: null
+    name: fluentd
+    hosts:
+      default: fluentd-logging
+    host_fqdn_override:
+      default: null
+    path:
+      default: null
+    scheme: 'http'
+    port:
+      service:
+        default: 24224
+      metrics:
+        default: 24220
 
 pod:
   user:
diff --git a/heat/templates/configmap-etc.yaml b/heat/templates/configmap-etc.yaml
index 7acde506df..59c10ced19 100644
--- a/heat/templates/configmap-etc.yaml
+++ b/heat/templates/configmap-etc.yaml
@@ -120,6 +120,18 @@ limitations under the License.
 {{- $_ := tuple "cloudformation" "internal" "api" . | include "helm-toolkit.endpoints.endpoint_port_lookup" | set .Values.conf.heat.heat_api_cfn "bind_port" -}}
 {{- end -}}
 
+{{- if and (empty .Values.conf.logging.handler_fluent) (has "fluent" .Values.conf.logging.handlers.keys) -}}
+{{- $fluentd_host := tuple "fluentd" "internal" $envAll | include "helm-toolkit.endpoints.hostname_namespaced_endpoint_lookup" }}
+{{- $fluentd_port := tuple "fluentd" "internal" "service" $envAll | include "helm-toolkit.endpoints.endpoint_port_lookup" }}
+{{- $fluent_args := printf "('openstack.heat', '%s', %s)" $fluentd_host $fluentd_port }}
+{{- $handler_fluent := dict "class" "fluent.handler.FluentHandler" "formatter" "fluent" "args" $fluent_args -}}
+{{- $_ := set .Values.conf.logging "handler_fluent" $handler_fluent -}}
+{{- end -}}
+
+{{- if and (empty .Values.conf.logging.formatter_fluent) (has "fluent" .Values.conf.logging.formatters.keys) -}}
+{{- $formatter_fluent := dict "class" "oslo_log.formatters.FluentFormatter" -}}
+{{- $_ := set .Values.conf.logging "formatter_fluent" $formatter_fluent -}}
+{{- end -}}
 ---
 apiVersion: v1
 kind: ConfigMap
@@ -130,6 +142,8 @@ data:
 {{ toYaml .Values.conf.rally_tests.tests | indent 4 }}
   heat.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.heat | indent 4 }}
+  logging.conf: |
+{{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.logging | indent 4 }}
   api-paste.ini: |
 {{ include "helm-toolkit.utils.to_ini" .Values.conf.paste | indent 4 }}
   policy.json: |
diff --git a/heat/templates/cron-job-engine-cleaner.yaml b/heat/templates/cron-job-engine-cleaner.yaml
index 6ec9bc28e7..ad7ad65049 100644
--- a/heat/templates/cron-job-engine-cleaner.yaml
+++ b/heat/templates/cron-job-engine-cleaner.yaml
@@ -62,6 +62,10 @@ spec:
                 mountPath: /etc/heat/heat.conf
                 subPath: heat.conf
                 readOnly: true
+              - name: heat-etc
+                mountPath: {{ .Values.conf.heat.DEFAULT.log_config_append }}
+                subPath: {{ base .Values.conf.heat.DEFAULT.log_config_append }}
+                readOnly: true
 {{ if $mounts_heat_engine_cleaner.volumeMounts }}{{ toYaml $mounts_heat_engine_cleaner.volumeMounts | indent 14 }}{{ end }}
           volumes:
           - name: etcheat
diff --git a/heat/templates/deployment-api.yaml b/heat/templates/deployment-api.yaml
index 5d751e9515..41f4caee72 100644
--- a/heat/templates/deployment-api.yaml
+++ b/heat/templates/deployment-api.yaml
@@ -83,6 +83,10 @@ spec:
               mountPath: /etc/heat/heat.conf
               subPath: heat.conf
               readOnly: true
+            - name: heat-etc
+              mountPath: {{ .Values.conf.heat.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.heat.DEFAULT.log_config_append }}
+              readOnly: true
             - name: heat-etc
               mountPath: /etc/heat/api-paste.ini
               subPath: api-paste.ini
diff --git a/heat/templates/deployment-cfn.yaml b/heat/templates/deployment-cfn.yaml
index 22d04c6f07..b8c7382e87 100644
--- a/heat/templates/deployment-cfn.yaml
+++ b/heat/templates/deployment-cfn.yaml
@@ -83,6 +83,10 @@ spec:
               mountPath: /etc/heat/heat.conf
               subPath: heat.conf
               readOnly: true
+            - name: heat-etc
+              mountPath: {{ .Values.conf.heat.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.heat.DEFAULT.log_config_append }}
+              readOnly: true
             - name: heat-etc
               mountPath: /etc/heat/api-paste.ini
               subPath: api-paste.ini
diff --git a/heat/templates/deployment-cloudwatch.yaml b/heat/templates/deployment-cloudwatch.yaml
index 74e343b115..135ec26aa5 100644
--- a/heat/templates/deployment-cloudwatch.yaml
+++ b/heat/templates/deployment-cloudwatch.yaml
@@ -83,6 +83,10 @@ spec:
               mountPath: /etc/heat/heat.conf
               subPath: heat.conf
               readOnly: true
+            - name: heat-etc
+              mountPath: {{ .Values.conf.heat.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.heat.DEFAULT.log_config_append }}
+              readOnly: true
             - name: heat-etc
               mountPath: /etc/heat/api-paste.ini
               subPath: api-paste.ini
diff --git a/heat/templates/deployment-engine.yaml b/heat/templates/deployment-engine.yaml
index f59e8d7fcb..50caaa599b 100644
--- a/heat/templates/deployment-engine.yaml
+++ b/heat/templates/deployment-engine.yaml
@@ -85,6 +85,10 @@ spec:
               mountPath: /etc/heat/heat.conf
               subPath: heat.conf
               readOnly: true
+            - name: heat-etc
+              mountPath: {{ .Values.conf.heat.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.heat.DEFAULT.log_config_append }}
+              readOnly: true
             - name: heat-etc
               mountPath: /etc/heat/policy.json
               subPath: policy.json
diff --git a/heat/templates/job-bootstrap.yaml b/heat/templates/job-bootstrap.yaml
index 904e2140c8..60b14983bd 100644
--- a/heat/templates/job-bootstrap.yaml
+++ b/heat/templates/job-bootstrap.yaml
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */}}
 
+
 {{- if and .Values.manifests.job_bootstrap .Values.bootstrap.enabled }}
-{{- $bootstrapJob := dict "envAll" . "serviceName" "heat" "keystoneUser" .Values.bootstrap.ks_user -}}
+{{- $bootstrapJob := dict "envAll" . "serviceName" "heat" "keystoneUser" .Values.bootstrap.ks_user "logConfigFile" .Values.conf.heat.DEFAULT.log_config_append -}}
 {{ $bootstrapJob | include "helm-toolkit.manifests.job_bootstrap" }}
 {{- end }}
diff --git a/heat/values.yaml b/heat/values.yaml
index 990cc2649d..553f941d2e 100644
--- a/heat/values.yaml
+++ b/heat/values.yaml
@@ -430,6 +430,7 @@ conf:
     resource_types:OS::Cinder::QoSSpecs: rule:project_admin
   heat:
     DEFAULT:
+      log_config_append: /etc/heat/logging.conf
       num_engine_workers: 1
       trusts_delegated_roles: ""
       host: heat-engine
@@ -465,6 +466,65 @@ conf:
       endpoint_type: internalURL
     oslo_messaging_notifications:
       driver: messagingv2
+  logging:
+    loggers:
+      keys:
+        - root
+        - heat
+    handlers:
+      keys:
+        - stdout
+        - stderr
+        - "null"
+    formatters:
+      keys:
+        - context
+        - default
+    logger_root:
+      level: WARNING
+      handlers: null
+    logger_heat:
+      level: INFO
+      handlers:
+        - stdout
+        - stderr
+      qualname: heat
+    logger_amqp:
+      level: WARNING
+      handlers: stderr
+      qualname: amqp
+    logger_amqplib:
+      level: WARNING
+      handlers: stderr
+      qualname: amqplib
+    logger_eventletwsgi:
+      level: WARNING
+      handlers: stderr
+      qualname: eventlet.wsgi.server
+    logger_sqlalchemy:
+      level: WARNING
+      handlers: stderr
+      qualname: sqlalchemy
+    logger_boto:
+      level: WARNING
+      handlers: stderr
+      qualname: boto
+    handler_null:
+      class: logging.NullHandler
+      formatter: default
+      args: ()
+    handler_stdout:
+      class: StreamHandler
+      args: (sys.stdout,)
+      formatter: context
+    handler_stderr:
+      class: StreamHandler
+      args: (sys.stderr,)
+      formatter: context
+    formatter_context:
+      class: oslo_log.formatters.ContextFormatter
+    formatter_default:
+      format: "%(message)s"
 
 network:
   api:
@@ -836,6 +896,21 @@ endpoints:
         default: 5672
       http:
         default: 15672
+  fluentd:
+    namespace: null
+    name: fluentd
+    hosts:
+      default: fluentd-logging
+    host_fqdn_override:
+      default: null
+    path:
+      default: null
+    scheme: 'http'
+    port:
+      service:
+        default: 24224
+      metrics:
+        default: 24220
 
 pod:
   user:
diff --git a/ironic/templates/configmap-etc.yaml b/ironic/templates/configmap-etc.yaml
index d9fff06223..74d3c16b23 100644
--- a/ironic/templates/configmap-etc.yaml
+++ b/ironic/templates/configmap-etc.yaml
@@ -184,6 +184,18 @@ limitations under the License.
 {{- $_ := set .Values.conf.ironic.api "port" (tuple "baremetal" "internal" "api" . | include "helm-toolkit.endpoints.endpoint_port_lookup") -}}
 {{- end -}}
 
+{{- if and (empty .Values.conf.logging.handler_fluent) (has "fluent" .Values.conf.logging.handlers.keys) -}}
+{{- $fluentd_host := tuple "fluentd" "internal" $envAll | include "helm-toolkit.endpoints.hostname_namespaced_endpoint_lookup" }}
+{{- $fluentd_port := tuple "fluentd" "internal" "service" $envAll | include "helm-toolkit.endpoints.endpoint_port_lookup" }}
+{{- $fluent_args := printf "('openstack.ironic', '%s', %s)" $fluentd_host $fluentd_port }}
+{{- $handler_fluent := dict "class" "fluent.handler.FluentHandler" "formatter" "fluent" "args" $fluent_args -}}
+{{- $_ := set .Values.conf.logging "handler_fluent" $handler_fluent -}}
+{{- end -}}
+
+{{- if and (empty .Values.conf.logging.formatter_fluent) (has "fluent" .Values.conf.logging.formatters.keys) -}}
+{{- $formatter_fluent := dict "class" "oslo_log.formatters.FluentFormatter" -}}
+{{- $_ := set .Values.conf.logging "formatter_fluent" $formatter_fluent -}}
+{{- end -}}
 ---
 apiVersion: v1
 kind: ConfigMap
@@ -192,6 +204,8 @@ metadata:
 data:
   ironic.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.ironic | indent 4 }}
+  logging.conf: |
+{{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.logging | indent 4 }}
   policy.json: |
 {{  toJson .Values.conf.policy | indent 4 }}
 {{- include "helm-toolkit.snippets.values_template_renderer" (dict "envAll" $envAll "template" .Values.conf.tftp_map_file "key" "tftp-map-file") | indent 2 }}
diff --git a/ironic/templates/deployment-api.yaml b/ironic/templates/deployment-api.yaml
index 47b53abfd5..1f8c5084fc 100644
--- a/ironic/templates/deployment-api.yaml
+++ b/ironic/templates/deployment-api.yaml
@@ -113,6 +113,10 @@ spec:
               mountPath: /etc/ironic/ironic.conf
               subPath: ironic.conf
               readOnly: true
+            - name: ironic-etc
+              mountPath: {{ .Values.conf.ironic.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.ironic.DEFAULT.log_config_append }}
+              readOnly: true
             - name: ironic-etc
               mountPath: /etc/ironic/policy.json
               subPath: policy.json
diff --git a/ironic/templates/job-bootstrap.yaml b/ironic/templates/job-bootstrap.yaml
index c33a17f338..fa1b10961f 100644
--- a/ironic/templates/job-bootstrap.yaml
+++ b/ironic/templates/job-bootstrap.yaml
@@ -15,6 +15,6 @@ limitations under the License.
 */}}
 
 {{- if and .Values.manifests.job_bootstrap .Values.bootstrap.enabled }}
-{{- $bootstrapJob := dict "envAll" . "serviceName" "ironic" "keystoneUser" .Values.bootstrap.ks_user -}}
+{{- $bootstrapJob := dict "envAll" . "serviceName" "ironic" "keystoneUser" .Values.bootstrap.ks_user "logConfigFile" .Values.conf.ironic.DEFAULT.log_config_append -}}
 {{ $bootstrapJob | include "helm-toolkit.manifests.job_bootstrap" }}
 {{- end }}
diff --git a/ironic/templates/statefulset-conductor.yaml b/ironic/templates/statefulset-conductor.yaml
index 56cdd522cc..928823e386 100644
--- a/ironic/templates/statefulset-conductor.yaml
+++ b/ironic/templates/statefulset-conductor.yaml
@@ -154,6 +154,10 @@ spec:
               mountPath: /etc/ironic/ironic.conf
               subPath: ironic.conf
               readOnly: true
+            - name: ironic-etc
+              mountPath: {{ .Values.conf.ironic.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.ironic.DEFAULT.log_config_append }}
+              readOnly: true
             - name: ironic-etc
               mountPath: /etc/ironic/policy.json
               subPath: policy.json
diff --git a/ironic/values.yaml b/ironic/values.yaml
index 8f7f8e6ecd..0a792c16ae 100644
--- a/ironic/values.yaml
+++ b/ironic/values.yaml
@@ -98,6 +98,7 @@ conf:
     }
   ironic:
     DEFAULT:
+      log_config_append: /etc/ironic/logging.conf
       enabled_drivers: agent_ipmitool
     api:
       port: null
@@ -136,6 +137,65 @@ conf:
       auth_type: password
     swift:
       auth_url: null
+  logging:
+    loggers:
+      keys:
+        - root
+        - ironic
+    handlers:
+      keys:
+        - stdout
+        - stderr
+        - "null"
+    formatters:
+      keys:
+        - context
+        - default
+    logger_root:
+      level: WARNING
+      handlers: null
+    logger_ironic:
+      level: INFO
+      handlers:
+        - stdout
+        - stderr
+      qualname: ironic
+    logger_amqp:
+      level: WARNING
+      handlers: stderr
+      qualname: amqp
+    logger_amqplib:
+      level: WARNING
+      handlers: stderr
+      qualname: amqplib
+    logger_eventletwsgi:
+      level: WARNING
+      handlers: stderr
+      qualname: eventlet.wsgi.server
+    logger_sqlalchemy:
+      level: WARNING
+      handlers: stderr
+      qualname: sqlalchemy
+    logger_boto:
+      level: WARNING
+      handlers: stderr
+      qualname: boto
+    handler_null:
+      class: logging.NullHandler
+      formatter: default
+      args: ()
+    handler_stdout:
+      class: StreamHandler
+      args: (sys.stdout,)
+      formatter: context
+    handler_stderr:
+      class: StreamHandler
+      args: (sys.stderr,)
+      formatter: context
+    formatter_context:
+      class: oslo_log.formatters.ContextFormatter
+    formatter_default:
+      format: "%(message)s"
 
 network:
   pxe:
@@ -463,6 +523,21 @@ endpoints:
     port:
       api:
         default: 8088
+  fluentd:
+    namespace: null
+    name: fluentd
+    hosts:
+      default: fluentd-logging
+    host_fqdn_override:
+      default: null
+    path:
+      default: null
+    scheme: 'http'
+    port:
+      service:
+        default: 24224
+      metrics:
+        default: 24220
 
 pod:
   affinity:
diff --git a/keystone/templates/configmap-etc.yaml b/keystone/templates/configmap-etc.yaml
index 9582c0dd9f..edbe3fbf92 100644
--- a/keystone/templates/configmap-etc.yaml
+++ b/keystone/templates/configmap-etc.yaml
@@ -29,6 +29,18 @@ limitations under the License.
 {{- $_ := tuple "oslo_cache" "internal" "memcache" . | include "helm-toolkit.endpoints.host_and_port_endpoint_uri_lookup" | set .Values.conf.keystone.cache "memcache_servers" -}}
 {{- end -}}
 
+{{- if and (empty .Values.conf.logging.handler_fluent) (has "fluent" .Values.conf.logging.handlers.keys) -}}
+{{- $fluentd_host := tuple "fluentd" "internal" $envAll | include "helm-toolkit.endpoints.hostname_namespaced_endpoint_lookup" }}
+{{- $fluentd_port := tuple "fluentd" "internal" "service" $envAll | include "helm-toolkit.endpoints.endpoint_port_lookup" }}
+{{- $fluent_args := printf "('openstack.keystone', '%s', %s)" $fluentd_host $fluentd_port }}
+{{- $handler_fluent := dict "class" "fluent.handler.FluentHandler" "formatter" "fluent" "args" $fluent_args -}}
+{{- $_ := set .Values.conf.logging "handler_fluent" $handler_fluent -}}
+{{- end -}}
+
+{{- if and (empty .Values.conf.logging.formatter_fluent) (has "fluent" .Values.conf.logging.formatters.keys) -}}
+{{- $formatter_fluent := dict "class" "oslo_log.formatters.FluentFormatter" -}}
+{{- $_ := set .Values.conf.logging "formatter_fluent" $formatter_fluent -}}
+{{- end -}}
 ---
 apiVersion: v1
 kind: ConfigMap
@@ -39,6 +51,8 @@ data:
 {{ toYaml .Values.conf.rally_tests.tests | indent 4 }}
   keystone.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.keystone | indent 4 }}
+  logging.conf: |
+{{ include "helm-toolkit.utils.to_oslo_conf" $envAll.Values.conf.logging | indent 4 }}
   keystone-paste.ini: |
 {{ include "helm-toolkit.utils.to_ini" .Values.conf.paste | indent 4 }}
   policy.json: |
diff --git a/keystone/templates/cron-job-credential-rotate.yaml b/keystone/templates/cron-job-credential-rotate.yaml
index b505e5b35e..20566feccc 100644
--- a/keystone/templates/cron-job-credential-rotate.yaml
+++ b/keystone/templates/cron-job-credential-rotate.yaml
@@ -99,6 +99,10 @@ spec:
                 mountPath: /etc/keystone/keystone.conf
                 subPath: keystone.conf
                 readOnly: true
+              - name: keystone-etc
+                mountPath: {{ .Values.conf.keystone.DEFAULT.log_config_append }}
+                subPath: {{ base .Values.conf.keystone.DEFAULT.log_config_append }}
+                readOnly: true
               - name: keystone-bin
                 mountPath: /tmp/fernet-manage.py
                 subPath: fernet-manage.py
diff --git a/keystone/templates/cron-job-fernet-rotate.yaml b/keystone/templates/cron-job-fernet-rotate.yaml
index 144a394bf5..8ef3d3db11 100644
--- a/keystone/templates/cron-job-fernet-rotate.yaml
+++ b/keystone/templates/cron-job-fernet-rotate.yaml
@@ -98,6 +98,10 @@ spec:
                 mountPath: /etc/keystone/keystone.conf
                 subPath: keystone.conf
                 readOnly: true
+              - name: keystone-etc
+                mountPath: {{ .Values.conf.keystone.DEFAULT.log_config_append }}
+                subPath: {{ base .Values.conf.keystone.DEFAULT.log_config_append }}
+                readOnly: true
               - name: keystone-bin
                 mountPath: /tmp/fernet-manage.py
                 subPath: fernet-manage.py
diff --git a/keystone/templates/deployment-api.yaml b/keystone/templates/deployment-api.yaml
index 7e7d0ddc14..86088d5dcb 100644
--- a/keystone/templates/deployment-api.yaml
+++ b/keystone/templates/deployment-api.yaml
@@ -85,6 +85,10 @@ spec:
             mountPath: /etc/keystone/keystone.conf
             subPath: keystone.conf
             readOnly: true
+          - name: keystone-etc
+            mountPath: {{ .Values.conf.keystone.DEFAULT.log_config_append }}
+            subPath: {{ base .Values.conf.keystone.DEFAULT.log_config_append }}
+            readOnly: true
           - name: keystone-etc
             mountPath: /etc/keystone/keystone-paste.ini
             subPath: keystone-paste.ini
diff --git a/keystone/templates/job-bootstrap.yaml b/keystone/templates/job-bootstrap.yaml
index c69d1286fa..9380722069 100644
--- a/keystone/templates/job-bootstrap.yaml
+++ b/keystone/templates/job-bootstrap.yaml
@@ -15,6 +15,6 @@ limitations under the License.
 */}}
 
 {{- if and .Values.manifests.job_bootstrap .Values.bootstrap.enabled }}
-{{- $bootstrapJob := dict "envAll" . "serviceName" "keystone" "keystoneUser" .Values.bootstrap.ks_user -}}
+{{- $bootstrapJob := dict "envAll" . "serviceName" "keystone" "keystoneUser" .Values.bootstrap.ks_user "logConfigFile" .Values.conf.keystone.DEFAULT.log_config_append -}}
 {{ $bootstrapJob | include "helm-toolkit.manifests.job_bootstrap" }}
 {{- end }}
diff --git a/keystone/templates/job-credential-setup.yaml b/keystone/templates/job-credential-setup.yaml
index 07b3b282ac..b0964453d9 100644
--- a/keystone/templates/job-credential-setup.yaml
+++ b/keystone/templates/job-credential-setup.yaml
@@ -91,6 +91,10 @@ spec:
             mountPath: /etc/keystone/keystone.conf
             subPath: keystone.conf
             readOnly: true
+          - name: keystone-etc
+            mountPath: {{ .Values.conf.keystone.DEFAULT.log_config_append }}
+            subPath: {{ base .Values.conf.keystone.DEFAULT.log_config_append }}
+            readOnly: true
           - name: keystone-bin
             mountPath: /tmp/fernet-manage.py
             subPath: fernet-manage.py
diff --git a/keystone/templates/job-domain-manage.yaml b/keystone/templates/job-domain-manage.yaml
index c515e7ef19..df0bd03a62 100644
--- a/keystone/templates/job-domain-manage.yaml
+++ b/keystone/templates/job-domain-manage.yaml
@@ -80,6 +80,10 @@ spec:
               mountPath: /etc/keystone/keystone.conf
               subPath: keystone.conf
               readOnly: true
+            - name: keystone-etc
+              mountPath: {{ .Values.conf.keystone.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.keystone.DEFAULT.log_config_append }}
+              readOnly: true
 {{- range $k, $v := .Values.conf.ks_domains }}
             - name: keystone-etc
               mountPath: {{ $envAll.Values.conf.keystone.identity.domain_config_dir | default "/etc/keystonedomains" }}/keystone.{{ $k }}.json
diff --git a/keystone/templates/job-fernet-setup.yaml b/keystone/templates/job-fernet-setup.yaml
index 0af3a799c0..58cd0a3bd5 100644
--- a/keystone/templates/job-fernet-setup.yaml
+++ b/keystone/templates/job-fernet-setup.yaml
@@ -92,6 +92,10 @@ spec:
             mountPath: /etc/keystone/keystone.conf
             subPath: keystone.conf
             readOnly: true
+          - name: keystone-etc
+            mountPath: {{ .Values.conf.keystone.DEFAULT.log_config_append }}
+            subPath: {{ base .Values.conf.keystone.DEFAULT.log_config_append }}
+            readOnly: true
           - name: keystone-bin
             mountPath: /tmp/fernet-manage.py
             subPath: fernet-manage.py
diff --git a/keystone/values.yaml b/keystone/values.yaml
index 3d6d901157..36352e83f9 100644
--- a/keystone/values.yaml
+++ b/keystone/values.yaml
@@ -337,6 +337,7 @@ jobs:
 conf:
   keystone:
     DEFAULT:
+      log_config_append: /etc/keystone/logging.conf
       max_token_size: 255
     token:
       provider: fernet
@@ -775,6 +776,65 @@ conf:
   sso_callback_template:
     override:
     append:
+  logging:
+    loggers:
+      keys:
+        - root
+        - keystone
+    handlers:
+      keys:
+        - stdout
+        - stderr
+        - "null"
+    formatters:
+      keys:
+        - context
+        - default
+    logger_root:
+      level: WARNING
+      handlers: null
+    logger_keystone:
+      level: INFO
+      handlers:
+        - stdout
+        - stderr
+      qualname: keystone
+    logger_amqp:
+      level: WARNING
+      handlers: stderr
+      qualname: amqp
+    logger_amqplib:
+      level: WARNING
+      handlers: stderr
+      qualname: amqplib
+    logger_eventletwsgi:
+      level: WARNING
+      handlers: stderr
+      qualname: eventlet.wsgi.server
+    logger_sqlalchemy:
+      level: WARNING
+      handlers: stderr
+      qualname: sqlalchemy
+    logger_boto:
+      level: WARNING
+      handlers: stderr
+      qualname: boto
+    handler_null:
+      class: logging.NullHandler
+      formatter: default
+      args: ()
+    handler_stdout:
+      class: StreamHandler
+      args: (sys.stdout,)
+      formatter: context
+    handler_stderr:
+      class: StreamHandler
+      args: (sys.stderr,)
+      formatter: context
+    formatter_context:
+      class: oslo_log.formatters.ContextFormatter
+    formatter_default:
+      format: "%(message)s"
 
 # Names of secrets used by bootstrap and environmental checks
 secrets:
@@ -900,6 +960,21 @@ endpoints:
           #   tls_req_cert: allow # Valid values: demand, never, allow
           #   tls_cacertfile: /etc/certs/tls.ca # abs path to the CA cert
           ca: null
+  fluentd:
+    namespace: null
+    name: fluentd
+    hosts:
+      default: fluentd-logging
+    host_fqdn_override:
+      default: null
+    path:
+      default: null
+    scheme: 'http'
+    port:
+      service:
+        default: 24224
+      metrics:
+        default: 24220
 
 manifests:
   configmap_bin: true
diff --git a/magnum/templates/configmap-etc.yaml b/magnum/templates/configmap-etc.yaml
index 98bbaaad10..19c7d9a1d6 100644
--- a/magnum/templates/configmap-etc.yaml
+++ b/magnum/templates/configmap-etc.yaml
@@ -73,6 +73,18 @@ limitations under the License.
 {{- $_ := set .Values.conf.magnum.trust "trustee_domain_admin_password" .Values.endpoints.identity.auth.magnum_stack_user.password -}}
 {{- end -}}
 
+{{- if and (empty .Values.conf.logging.handler_fluent) (has "fluent" .Values.conf.logging.handlers.keys) -}}
+{{- $fluentd_host := tuple "fluentd" "internal" $envAll | include "helm-toolkit.endpoints.hostname_namespaced_endpoint_lookup" }}
+{{- $fluentd_port := tuple "fluentd" "internal" "service" $envAll | include "helm-toolkit.endpoints.endpoint_port_lookup" }}
+{{- $fluent_args := printf "('openstack.magnum', '%s', %s)" $fluentd_host $fluentd_port }}
+{{- $handler_fluent := dict "class" "fluent.handler.FluentHandler" "formatter" "fluent" "args" $fluent_args -}}
+{{- $_ := set .Values.conf.logging "handler_fluent" $handler_fluent -}}
+{{- end -}}
+
+{{- if and (empty .Values.conf.logging.formatter_fluent) (has "fluent" .Values.conf.logging.formatters.keys) -}}
+{{- $formatter_fluent := dict "class" "oslo_log.formatters.FluentFormatter" -}}
+{{- $_ := set .Values.conf.logging "formatter_fluent" $formatter_fluent -}}
+{{- end -}}
 ---
 apiVersion: v1
 kind: ConfigMap
@@ -81,6 +93,8 @@ metadata:
 data:
   magnum.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.magnum | indent 4 }}
+  logging.conf: |
+{{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.logging | indent 4 }}
   api-paste.ini: |
 {{ include "helm-toolkit.utils.to_ini" .Values.conf.paste | indent 4 }}
   policy.json: |
diff --git a/magnum/templates/deployment-api.yaml b/magnum/templates/deployment-api.yaml
index ceac6edd27..a859df5f60 100644
--- a/magnum/templates/deployment-api.yaml
+++ b/magnum/templates/deployment-api.yaml
@@ -89,6 +89,10 @@ spec:
               mountPath: /etc/magnum/magnum.conf
               subPath: magnum.conf
               readOnly: true
+            - name: magnum-etc
+              mountPath: {{ .Values.conf.magnum.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.magnum.DEFAULT.log_config_append }}
+              readOnly: true
             - name: magnum-etc
               mountPath: /etc/magnum/api-paste.ini
               subPath: api-paste.ini
diff --git a/magnum/templates/job-bootstrap.yaml b/magnum/templates/job-bootstrap.yaml
index 668f46c350..e703913ee2 100644
--- a/magnum/templates/job-bootstrap.yaml
+++ b/magnum/templates/job-bootstrap.yaml
@@ -15,6 +15,6 @@ limitations under the License.
 */}}
 
 {{- if and .Values.manifests.job_bootstrap .Values.bootstrap.enabled }}
-{{- $bootstrapJob := dict "envAll" . "serviceName" "magnum" "keystoneUser" .Values.bootstrap.ks_user -}}
+{{- $bootstrapJob := dict "envAll" . "serviceName" "magnum" "keystoneUser" .Values.bootstrap.ks_user "logConfigFile" .Values.conf.magnum.DEFAULT.log_config_append -}}
 {{ $bootstrapJob | include "helm-toolkit.manifests.job_bootstrap" }}
 {{- end }}
diff --git a/magnum/templates/statefulset-conductor.yaml b/magnum/templates/statefulset-conductor.yaml
index 30033cf889..0762cae2a1 100644
--- a/magnum/templates/statefulset-conductor.yaml
+++ b/magnum/templates/statefulset-conductor.yaml
@@ -86,6 +86,10 @@ spec:
               mountPath: /etc/magnum/magnum.conf
               subPath: magnum.conf
               readOnly: true
+            - name: magnum-etc
+              mountPath: {{ .Values.conf.magnum.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.magnum.DEFAULT.log_config_append }}
+              readOnly: true
             - name: magnum-etc
               mountPath: /etc/magnum/policy.json
               subPath: policy.json
diff --git a/magnum/values.yaml b/magnum/values.yaml
index a04fa88fbc..774dc9f9e0 100644
--- a/magnum/values.yaml
+++ b/magnum/values.yaml
@@ -114,6 +114,7 @@ conf:
     magnum-service:get_all: rule:admin_api
   magnum:
     DEFAULT:
+      log_config_append: /etc/magnum/logging.conf
       transport_url: null
     oslo_messaging_notifications:
       driver: messaging
@@ -134,6 +135,65 @@ conf:
       # via the endpoints section.
       port: null
       host: 0.0.0.0
+  logging:
+    loggers:
+      keys:
+        - root
+        - magnum
+    handlers:
+      keys:
+        - stdout
+        - stderr
+        - "null"
+    formatters:
+      keys:
+        - context
+        - default
+    logger_root:
+      level: WARNING
+      handlers: null
+    logger_magnum:
+      level: INFO
+      handlers:
+        - stdout
+        - stderr
+      qualname: magnum
+    logger_amqp:
+      level: WARNING
+      handlers: stderr
+      qualname: amqp
+    logger_amqplib:
+      level: WARNING
+      handlers: stderr
+      qualname: amqplib
+    logger_eventletwsgi:
+      level: WARNING
+      handlers: stderr
+      qualname: eventlet.wsgi.server
+    logger_sqlalchemy:
+      level: WARNING
+      handlers: stderr
+      qualname: sqlalchemy
+    logger_boto:
+      level: WARNING
+      handlers: stderr
+      qualname: boto
+    handler_null:
+      class: logging.NullHandler
+      formatter: default
+      args: ()
+    handler_stdout:
+      class: StreamHandler
+      args: (sys.stdout,)
+      formatter: context
+    handler_stderr:
+      class: StreamHandler
+      args: (sys.stderr,)
+      formatter: context
+    formatter_context:
+      class: oslo_log.formatters.ContextFormatter
+    formatter_default:
+      format: "%(message)s"
 
 network:
   api:
@@ -402,6 +462,21 @@ endpoints:
         default: 5672
       http:
         default: 15672
+  fluentd:
+    namespace: null
+    name: fluentd
+    hosts:
+      default: fluentd-logging
+    host_fqdn_override:
+      default: null
+    path:
+      default: null
+    scheme: 'http'
+    port:
+      service:
+        default: 24224
+      metrics:
+        default: 24220
 
 pod:
   user:
diff --git a/mistral/templates/configmap-etc.yaml b/mistral/templates/configmap-etc.yaml
index 53dde0e0e3..f367b10b91 100644
--- a/mistral/templates/configmap-etc.yaml
+++ b/mistral/templates/configmap-etc.yaml
@@ -63,6 +63,18 @@ limitations under the License.
 {{- $_ := tuple "workflowv2" "internal" "api" . | include "helm-toolkit.endpoints.endpoint_port_lookup" | set .Values.conf.mistral.api "port" -}}
 {{- end -}}
 
+{{- if and (empty .Values.conf.logging.handler_fluent) (has "fluent" .Values.conf.logging.handlers.keys) -}}
+{{- $fluentd_host := tuple "fluentd" "internal" $envAll | include "helm-toolkit.endpoints.hostname_namespaced_endpoint_lookup" }}
+{{- $fluentd_port := tuple "fluentd" "internal" "service" $envAll | include "helm-toolkit.endpoints.endpoint_port_lookup" }}
+{{- $fluent_args := printf "('openstack.mistral', '%s', %s)" $fluentd_host $fluentd_port }}
+{{- $handler_fluent := dict "class" "fluent.handler.FluentHandler" "formatter" "fluent" "args" $fluent_args -}}
+{{- $_ := set .Values.conf.logging "handler_fluent" $handler_fluent -}}
+{{- end -}}
+
+{{- if and (empty .Values.conf.logging.formatter_fluent) (has "fluent" .Values.conf.logging.formatters.keys) -}}
+{{- $formatter_fluent := dict "class" "oslo_log.formatters.FluentFormatter" -}}
+{{- $_ := set .Values.conf.logging "formatter_fluent" $formatter_fluent -}}
+{{- end -}}
 ---
 apiVersion: v1
 kind: ConfigMap
@@ -73,6 +85,8 @@ data:
 {{ toYaml .Values.conf.rally_tests.tests | indent 4 }}
   mistral.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.mistral | indent 4 }}
+  logging.conf: |
+{{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.logging | indent 4 }}
   policy.json: |
 {{  toJson .Values.conf.policy | indent 4 }}
 {{- range $key, $value := $envAll.Values.conf.rally_tests.templates }}
diff --git a/mistral/templates/deployment-api.yaml b/mistral/templates/deployment-api.yaml
index ce63f3a5cd..7d519dd8c0 100644
--- a/mistral/templates/deployment-api.yaml
+++ b/mistral/templates/deployment-api.yaml
@@ -83,6 +83,10 @@ spec:
               mountPath: /etc/mistral/mistral.conf
               subPath: mistral.conf
               readOnly: true
+            - name: mistral-etc
+              mountPath: {{ .Values.conf.mistral.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.mistral.DEFAULT.log_config_append }}
+              readOnly: true
             - name: mistral-etc
               mountPath: /etc/mistral/policy.json
               subPath: policy.json
diff --git a/mistral/templates/deployment-executor.yaml b/mistral/templates/deployment-executor.yaml
index d07c31a141..4623dabcd0 100644
--- a/mistral/templates/deployment-executor.yaml
+++ b/mistral/templates/deployment-executor.yaml
@@ -69,6 +69,10 @@ spec:
               mountPath: /etc/mistral/mistral.conf
               subPath: mistral.conf
               readOnly: true
+            - name: mistral-etc
+              mountPath: {{ .Values.conf.mistral.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.mistral.DEFAULT.log_config_append }}
+              readOnly: true
 {{ if $mounts_mistral_executor.volumeMounts }}{{ toYaml $mounts_mistral_executor.volumeMounts | indent 12 }}{{ end }}
       volumes:
         - name: pod-etc-mistral
diff --git a/mistral/templates/job-bootstrap.yaml b/mistral/templates/job-bootstrap.yaml
index 00e463e956..31efd8c8df 100644
--- a/mistral/templates/job-bootstrap.yaml
+++ b/mistral/templates/job-bootstrap.yaml
@@ -15,6 +15,6 @@ limitations under the License.
 */}}
 
 {{- if and .Values.manifests.job_bootstrap .Values.bootstrap.enabled }}
-{{- $bootstrapJob := dict "envAll" . "serviceName" "mistral" "keystoneUser" .Values.bootstrap.ks_user -}}
+{{- $bootstrapJob := dict "envAll" . "serviceName" "mistral" "keystoneUser" .Values.bootstrap.ks_user "logConfigFile" .Values.conf.mistral.DEFAULT.log_config_append -}}
 {{ $bootstrapJob | include "helm-toolkit.manifests.job_bootstrap" }}
 {{- end }}
diff --git a/mistral/templates/statefulset-engine.yaml b/mistral/templates/statefulset-engine.yaml
index ea02336faf..9a77b22a6f 100644
--- a/mistral/templates/statefulset-engine.yaml
+++ b/mistral/templates/statefulset-engine.yaml
@@ -66,6 +66,10 @@ spec:
               mountPath: /etc/mistral/mistral.conf
               subPath: mistral.conf
               readOnly: true
+            - name: mistral-etc
+              mountPath: {{ .Values.conf.mistral.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.mistral.DEFAULT.log_config_append }}
+              readOnly: true
 {{ if $mounts_mistral_engine.volumeMounts }}{{ toYaml $mounts_mistral_engine.volumeMounts | indent 12 }}{{ end }}
       volumes:
         - name: pod-etc-mistral
diff --git a/mistral/templates/statefulset-event-engine.yaml b/mistral/templates/statefulset-event-engine.yaml
index b7f2c97f7d..3b043bcd23 100644
--- a/mistral/templates/statefulset-event-engine.yaml
+++ b/mistral/templates/statefulset-event-engine.yaml
@@ -66,6 +66,10 @@ spec:
               mountPath: /etc/mistral/mistral.conf
               subPath: mistral.conf
               readOnly: true
+            - name: mistral-etc
+              mountPath: {{ .Values.conf.mistral.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.mistral.DEFAULT.log_config_append }}
+              readOnly: true
 {{ if $mounts_mistral_event_engine.volumeMounts }}{{ toYaml $mounts_mistral_event_engine.volumeMounts | indent 12 }}{{ end }}
       volumes:
         - name: pod-etc-mistral
diff --git a/mistral/values.yaml b/mistral/values.yaml
index c886950027..736e3c415a 100644
--- a/mistral/values.yaml
+++ b/mistral/values.yaml
@@ -315,6 +315,21 @@ endpoints:
     port:
       memcache:
         default: 11211
+  fluentd:
+    namespace: null
+    name: fluentd
+    hosts:
+      default: fluentd-logging
+    host_fqdn_override:
+      default: null
+    path:
+      default: null
+    scheme: 'http'
+    port:
+      service:
+        default: 24224
+      metrics:
+        default: 24220
 
 conf:
   rally_tests:
@@ -437,6 +452,7 @@ conf:
     event_triggers:update: rule:admin_or_owner
   mistral:
     DEFAULT:
+      log_config_append: /etc/mistral/logging.conf
       transport_url: null
     api:
       # NOTE(portdirect): the bind port should not be defined, and is manipulated
@@ -451,6 +467,65 @@ conf:
       auth_type: password
       auth_version: v3
       memcache_security_strategy: ENCRYPT
+  logging:
+    loggers:
+      keys:
+        - root
+        - mistral
+    handlers:
+      keys:
+        - stdout
+        - stderr
+        - "null"
+    formatters:
+      keys:
+        - context
+        - default
+    logger_root:
+      level: WARNING
+      handlers: null
+    logger_mistral:
+      level: INFO
+      handlers:
+        - stdout
+        - stderr
+      qualname: mistral
+    logger_amqp:
+      level: WARNING
+      handlers: stderr
+      qualname: amqp
+    logger_amqplib:
+      level: WARNING
+      handlers: stderr
+      qualname: amqplib
+    logger_eventletwsgi:
+      level: WARNING
+      handlers: stderr
+      qualname: eventlet.wsgi.server
+    logger_sqlalchemy:
+      level: WARNING
+      handlers: stderr
+      qualname: sqlalchemy
+    logger_boto:
+      level: WARNING
+      handlers: stderr
+      qualname: boto
+    handler_null:
+      class: logging.NullHandler
+      formatter: default
+      args: ()
+    handler_stdout:
+      class: StreamHandler
+      args: (sys.stdout,)
+      formatter: context
+    handler_stderr:
+      class: StreamHandler
+      args: (sys.stderr,)
+      formatter: context
+    formatter_context:
+      class: oslo_log.formatters.ContextFormatter
+    formatter_default:
+      format: "%(message)s"
 
 pod:
   user:
diff --git a/neutron/templates/configmap-etc.yaml b/neutron/templates/configmap-etc.yaml
index 282cc73bfd..88a26c9871 100644
--- a/neutron/templates/configmap-etc.yaml
+++ b/neutron/templates/configmap-etc.yaml
@@ -157,6 +157,18 @@ just set it along with nova_metadata_host.
 {{- $_ := tuple "network" "internal" "api" . | include "helm-toolkit.endpoints.endpoint_port_lookup" | set .Values.conf.neutron.DEFAULT "bind_port" -}}
 {{- end -}}
 
+{{- if and (empty .Values.conf.logging.handler_fluent) (has "fluent" .Values.conf.logging.handlers.keys) -}}
+{{- $fluentd_host := tuple "fluentd" "internal" $envAll | include "helm-toolkit.endpoints.hostname_namespaced_endpoint_lookup" }}
+{{- $fluentd_port := tuple "fluentd" "internal" "service" $envAll | include "helm-toolkit.endpoints.endpoint_port_lookup" }}
+{{- $fluent_args := printf "('openstack.neutron', '%s', %s)" $fluentd_host $fluentd_port }}
+{{- $handler_fluent := dict "class" "fluent.handler.FluentHandler" "formatter" "fluent" "args" $fluent_args -}}
+{{- $_ := set .Values.conf.logging "handler_fluent" $handler_fluent -}}
+{{- end -}}
+
+{{- if and (empty .Values.conf.logging.formatter_fluent) (has "fluent" .Values.conf.logging.formatters.keys) -}}
+{{- $formatter_fluent := dict "class" "oslo_log.formatters.FluentFormatter" -}}
+{{- $_ := set .Values.conf.logging "formatter_fluent" $formatter_fluent -}}
+{{- end -}}
 ---
 apiVersion: v1
 kind: ConfigMap
@@ -171,6 +183,8 @@ data:
 {{ toJson $envAll.Values.conf.policy | indent 4 }}
   neutron.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" $envAll.Values.conf.neutron | indent 4 }}
+  logging.conf: |
+{{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.logging | indent 4 }}
   dhcp_agent.ini: |
 {{ include "helm-toolkit.utils.to_oslo_conf" $envAll.Values.conf.dhcp_agent | indent 4 }}
   l3_agent.ini: |
diff --git a/neutron/templates/daemonset-dhcp-agent.yaml b/neutron/templates/daemonset-dhcp-agent.yaml
index 5dfac051e2..91688f79d7 100644
--- a/neutron/templates/daemonset-dhcp-agent.yaml
+++ b/neutron/templates/daemonset-dhcp-agent.yaml
@@ -69,6 +69,10 @@ spec:
               mountPath: /etc/neutron/neutron.conf
               subPath: neutron.conf
               readOnly: true
+            - name: neutron-etc
+              mountPath: {{ .Values.conf.neutron.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.neutron.DEFAULT.log_config_append }}
+              readOnly: true
             - name: neutron-etc
               mountPath: /etc/neutron/plugins/ml2/ml2_conf.ini
               subPath: ml2_conf.ini
diff --git a/neutron/templates/daemonset-l3-agent.yaml b/neutron/templates/daemonset-l3-agent.yaml
index 0296a0280d..60647023a0 100644
--- a/neutron/templates/daemonset-l3-agent.yaml
+++ b/neutron/templates/daemonset-l3-agent.yaml
@@ -69,6 +69,10 @@ spec:
               mountPath: /etc/neutron/neutron.conf
               subPath: neutron.conf
               readOnly: true
+            - name: neutron-etc
+              mountPath: {{ .Values.conf.neutron.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.neutron.DEFAULT.log_config_append }}
+              readOnly: true
             - name: neutron-etc
               mountPath: /etc/neutron/plugins/ml2/ml2_conf.ini
               subPath: ml2_conf.ini
diff --git a/neutron/templates/daemonset-lb-agent.yaml b/neutron/templates/daemonset-lb-agent.yaml
index 5096fefa21..91572cc61b 100644
--- a/neutron/templates/daemonset-lb-agent.yaml
+++ b/neutron/templates/daemonset-lb-agent.yaml
@@ -145,6 +145,10 @@ spec:
               mountPath: /etc/neutron/neutron.conf
               subPath: neutron.conf
               readOnly: true
+            - name: neutron-etc
+              mountPath: {{ .Values.conf.neutron.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.neutron.DEFAULT.log_config_append }}
+              readOnly: true
             - name: neutron-etc
               mountPath: /etc/neutron/plugins/ml2/ml2_conf.ini
               subPath: ml2_conf.ini
diff --git a/neutron/templates/daemonset-metadata-agent.yaml b/neutron/templates/daemonset-metadata-agent.yaml
index d466ec45a7..779ff8522b 100644
--- a/neutron/templates/daemonset-metadata-agent.yaml
+++ b/neutron/templates/daemonset-metadata-agent.yaml
@@ -90,6 +90,10 @@ spec:
               mountPath: /etc/neutron/neutron.conf
               subPath: neutron.conf
               readOnly: true
+            - name: neutron-etc
+              mountPath: {{ .Values.conf.neutron.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.neutron.DEFAULT.log_config_append }}
+              readOnly: true
             - name: neutron-etc
               mountPath: /etc/neutron/plugins/ml2/ml2_conf.ini
               subPath: ml2_conf.ini
diff --git a/neutron/templates/daemonset-ovs-agent.yaml b/neutron/templates/daemonset-ovs-agent.yaml
index e8a0972b48..884540ea07 100644
--- a/neutron/templates/daemonset-ovs-agent.yaml
+++ b/neutron/templates/daemonset-ovs-agent.yaml
@@ -147,6 +147,10 @@ spec:
               mountPath: /etc/neutron/neutron.conf
               subPath: neutron.conf
               readOnly: true
+            - name: neutron-etc
+              mountPath: {{ .Values.conf.neutron.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.neutron.DEFAULT.log_config_append }}
+              readOnly: true
             - name: neutron-etc
               mountPath: /etc/neutron/plugins/ml2/ml2_conf.ini
               subPath: ml2_conf.ini
diff --git a/neutron/templates/daemonset-sriov-agent.yaml b/neutron/templates/daemonset-sriov-agent.yaml
index 049df5bde1..706fc76f8a 100644
--- a/neutron/templates/daemonset-sriov-agent.yaml
+++ b/neutron/templates/daemonset-sriov-agent.yaml
@@ -122,6 +122,10 @@ spec:
               mountPath: /etc/neutron/neutron.conf
               subPath: neutron.conf
               readOnly: true
+            - name: neutron-etc
+              mountPath: {{ .Values.conf.neutron.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.neutron.DEFAULT.log_config_append }}
+              readOnly: true
             - name: neutron-etc
               mountPath: /etc/neutron/plugins/ml2/ml2_conf.ini
               subPath: ml2_conf.ini
diff --git a/neutron/templates/deployment-server.yaml b/neutron/templates/deployment-server.yaml
index 1e209abea5..03d3835f6c 100644
--- a/neutron/templates/deployment-server.yaml
+++ b/neutron/templates/deployment-server.yaml
@@ -84,6 +84,10 @@ spec:
               mountPath: /etc/neutron/neutron.conf
               subPath: neutron.conf
               readOnly: true
+            - name: neutron-etc
+              mountPath: {{ .Values.conf.neutron.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.neutron.DEFAULT.log_config_append }}
+              readOnly: true
             - name: neutron-etc
               mountPath: /etc/neutron/plugins/ml2/ml2_conf.ini
               subPath: ml2_conf.ini
diff --git a/neutron/templates/job-bootstrap.yaml b/neutron/templates/job-bootstrap.yaml
index d705809aef..b48c8661e8 100644
--- a/neutron/templates/job-bootstrap.yaml
+++ b/neutron/templates/job-bootstrap.yaml
@@ -15,6 +15,6 @@ limitations under the License.
 */}}
 
 {{- if and .Values.manifests.job_bootstrap .Values.bootstrap.enabled }}
-{{- $bootstrapJob := dict "envAll" . "serviceName" "neutron" "keystoneUser" .Values.bootstrap.ks_user -}}
+{{- $bootstrapJob := dict "envAll" . "serviceName" "neutron" "keystoneUser" .Values.bootstrap.ks_user "logConfigFile" .Values.conf.neutron.DEFAULT.log_config_append -}}
 {{ $bootstrapJob | include "helm-toolkit.manifests.job_bootstrap" }}
 {{- end }}
diff --git a/neutron/values.yaml b/neutron/values.yaml
index d3fb64d719..e7c6c23e14 100644
--- a/neutron/values.yaml
+++ b/neutron/values.yaml
@@ -1435,6 +1435,7 @@ conf:
           # Adjust to suit local requirements.
   neutron:
     DEFAULT:
+      log_config_append: /etc/neutron/logging.conf
       #NOTE(portdirect): the bind port should not be defined, and is manipulated
       # via the endpoints section.
       bind_port: null
@@ -1472,6 +1473,65 @@ conf:
       memcache_security_strategy: ENCRYPT
       auth_type: password
       auth_version: v3
+  logging:
+    loggers:
+      keys:
+        - root
+        - neutron
+    handlers:
+      keys:
+        - stdout
+        - stderr
+        - "null"
+    formatters:
+      keys:
+        - context
+        - default
+    logger_root:
+      level: WARNING
+      handlers: null
+    logger_neutron:
+      level: INFO
+      handlers:
+        - stdout
+        - stderr
+      qualname: neutron
+    logger_amqp:
+      level: WARNING
+      handlers: stderr
+      qualname: amqp
+    logger_amqplib:
+      level: WARNING
+      handlers: stderr
+      qualname: amqplib
+    logger_eventletwsgi:
+      level: WARNING
+      handlers: stderr
+      qualname: eventlet.wsgi.server
+    logger_sqlalchemy:
+      level: WARNING
+      handlers: stderr
+      qualname: sqlalchemy
+    logger_boto:
+      level: WARNING
+      handlers: stderr
+      qualname: boto
+    handler_null:
+      class: logging.NullHandler
+      formatter: default
+      args: ()
+    handler_stdout:
+      class: StreamHandler
+      args: (sys.stdout,)
+      formatter: context
+    handler_stderr:
+      class: StreamHandler
+      args: (sys.stderr,)
+      formatter: context
+    formatter_context:
+      class: oslo_log.formatters.ContextFormatter
+    formatter_default:
+      format: "%(message)s"
   plugins:
     ml2_conf:
       ml2:
@@ -1720,6 +1780,21 @@ endpoints:
       api:
         default: 9696
         public: 80
+  fluentd:
+    namespace: osh-infra
+    name: fluentd
+    hosts:
+      default: fluentd-logging
+    host_fqdn_override:
+      default: null
+    path:
+      default: null
+    scheme: 'http'
+    port:
+      service:
+        default: 24224
+      metrics:
+        default: 24220
 
 manifests:
   configmap_bin: true
diff --git a/nova/templates/configmap-etc.yaml b/nova/templates/configmap-etc.yaml
index 40983eb852..ea70c13827 100644
--- a/nova/templates/configmap-etc.yaml
+++ b/nova/templates/configmap-etc.yaml
@@ -200,6 +200,18 @@ limitations under the License.
 {{- $_ := tuple "compute" "internal" "api" . | include "helm-toolkit.endpoints.endpoint_port_lookup" | set .Values.conf.nova.DEFAULT "osapi_compute_listen_port" -}}
 {{- end -}}
 
+{{- if and (empty .Values.conf.logging.handler_fluent) (has "fluent" .Values.conf.logging.handlers.keys) -}}
+{{- $fluentd_host := tuple "fluentd" "internal" $envAll | include "helm-toolkit.endpoints.hostname_namespaced_endpoint_lookup" }}
+{{- $fluentd_port := tuple "fluentd" "internal" "service" $envAll | include "helm-toolkit.endpoints.endpoint_port_lookup" }}
+{{- $fluent_args := printf "('openstack.nova', '%s', %s)" $fluentd_host $fluentd_port }}
+{{- $handler_fluent := dict "class" "fluent.handler.FluentHandler" "formatter" "fluent" "args" $fluent_args -}}
+{{- $_ := set .Values.conf.logging "handler_fluent" $handler_fluent -}}
+{{- end -}}
+
+{{- if and (empty .Values.conf.logging.formatter_fluent) (has "fluent" .Values.conf.logging.formatters.keys) -}}
+{{- $formatter_fluent := dict "class" "oslo_log.formatters.FluentFormatter" -}}
+{{- $_ := set .Values.conf.logging "formatter_fluent" $formatter_fluent -}}
+{{- end -}}
 ---
 apiVersion: v1
 kind: ConfigMap
@@ -224,6 +236,8 @@ data:
 {{- tuple .Values.conf.rootwrap_filters.network "etc/rootwrap.d/_network.filters.tpl" . | include "helm-toolkit.utils.configmap_templater" }}
   nova.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.nova | indent 4 }}
+  logging.conf: |
+{{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.logging | indent 4 }}
   nova-ironic.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.nova_ironic | indent 4 }}
   wsgi-nova-placement.conf: |
diff --git a/nova/templates/cron-job-cell-setup.yaml b/nova/templates/cron-job-cell-setup.yaml
index eac6297227..a59b37483e 100644
--- a/nova/templates/cron-job-cell-setup.yaml
+++ b/nova/templates/cron-job-cell-setup.yaml
@@ -62,6 +62,10 @@ spec:
                   mountPath: /etc/nova/nova.conf
                   subPath: nova.conf
                   readOnly: true
+                - name: nova-etc
+                  mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+                  subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+                  readOnly: true
                 - name: nova-etc
                   mountPath: /etc/nova/policy.yaml
                   subPath: policy.yaml
diff --git a/nova/templates/daemonset-compute.yaml b/nova/templates/daemonset-compute.yaml
index 4627836bed..1f0a7be3e3 100644
--- a/nova/templates/daemonset-compute.yaml
+++ b/nova/templates/daemonset-compute.yaml
@@ -176,6 +176,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+              readOnly: true
             - name: nova-etc
               mountPath: /etc/nova/api-paste.ini
               subPath: api-paste.ini
diff --git a/nova/templates/deployment-api-metadata.yaml b/nova/templates/deployment-api-metadata.yaml
index f6705e575e..183edec78d 100644
--- a/nova/templates/deployment-api-metadata.yaml
+++ b/nova/templates/deployment-api-metadata.yaml
@@ -110,6 +110,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+              readOnly: true
             - name: nova-etc
               mountPath: /etc/nova/api-paste.ini
               subPath: api-paste.ini
diff --git a/nova/templates/deployment-api-osapi.yaml b/nova/templates/deployment-api-osapi.yaml
index 91317bd970..4e1f40ba7f 100644
--- a/nova/templates/deployment-api-osapi.yaml
+++ b/nova/templates/deployment-api-osapi.yaml
@@ -81,6 +81,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+              readOnly: true
             - name: nova-etc
               mountPath: /etc/nova/api-paste.ini
               subPath: api-paste.ini
diff --git a/nova/templates/deployment-conductor.yaml b/nova/templates/deployment-conductor.yaml
index 837b2a65b2..823e8cb43c 100644
--- a/nova/templates/deployment-conductor.yaml
+++ b/nova/templates/deployment-conductor.yaml
@@ -67,6 +67,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+              readOnly: true
             - name: nova-etc
               mountPath: /etc/nova/policy.yaml
               subPath: policy.yaml
diff --git a/nova/templates/deployment-consoleauth.yaml b/nova/templates/deployment-consoleauth.yaml
index 1a481f6ee6..7083ad1b54 100644
--- a/nova/templates/deployment-consoleauth.yaml
+++ b/nova/templates/deployment-consoleauth.yaml
@@ -67,6 +67,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+              readOnly: true
             - name: nova-etc
               mountPath: /etc/nova/policy.yaml
               subPath: policy.yaml
diff --git a/nova/templates/deployment-novncproxy.yaml b/nova/templates/deployment-novncproxy.yaml
index 85d4354a01..3f523dadc0 100644
--- a/nova/templates/deployment-novncproxy.yaml
+++ b/nova/templates/deployment-novncproxy.yaml
@@ -68,6 +68,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+              readOnly: true
             - name: pod-shared
               mountPath: /tmp/pod-shared
         - name: nova-novncproxy-init-assets
@@ -100,6 +104,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: /etc/nova/logging.conf
+              subPath: logging.conf
+              readOnly: true
             - name: pod-usr-share-novnc
               mountPath: /usr/share/novnc
               readOnly: true
diff --git a/nova/templates/deployment-placement.yaml b/nova/templates/deployment-placement.yaml
index b59f8f2c59..f96500b4ec 100644
--- a/nova/templates/deployment-placement.yaml
+++ b/nova/templates/deployment-placement.yaml
@@ -84,6 +84,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+              readOnly: true
             - name: nova-etc
               mountPath: /etc/nova/api-paste.ini
               subPath: api-paste.ini
diff --git a/nova/templates/deployment-scheduler.yaml b/nova/templates/deployment-scheduler.yaml
index c0a2369532..f042ba0ff8 100644
--- a/nova/templates/deployment-scheduler.yaml
+++ b/nova/templates/deployment-scheduler.yaml
@@ -67,6 +67,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+              readOnly: true
             - name: nova-etc
               mountPath: /etc/nova/policy.yaml
               subPath: policy.yaml
diff --git a/nova/templates/deployment-spiceproxy.yaml b/nova/templates/deployment-spiceproxy.yaml
index 94d80bb458..8df7f82275 100644
--- a/nova/templates/deployment-spiceproxy.yaml
+++ b/nova/templates/deployment-spiceproxy.yaml
@@ -68,6 +68,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+              readOnly: true
             - name: pod-shared
               mountPath: /tmp/pod-shared
         - name: nova-spiceproxy-init-assets
@@ -100,6 +104,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+              readOnly: true
             - name: pod-usr-share-spice-html5
               mountPath: /usr/share/spice-html5
               readOnly: true
diff --git a/nova/templates/job-bootstrap.yaml b/nova/templates/job-bootstrap.yaml
index 6620f2d776..973ca816c0 100644
--- a/nova/templates/job-bootstrap.yaml
+++ b/nova/templates/job-bootstrap.yaml
@@ -15,6 +15,6 @@ limitations under the License.
 */}}
 
 {{- if and .Values.manifests.job_bootstrap .Values.bootstrap.enabled }}
-{{- $bootstrapJob := dict "envAll" . "serviceName" "nova" "keystoneUser" .Values.bootstrap.ks_user -}}
+{{- $bootstrapJob := dict "envAll" . "serviceName" "nova" "keystoneUser" .Values.bootstrap.ks_user "logConfigFile" .Values.conf.nova.DEFAULT.log_config_append -}}
 {{ $bootstrapJob | include "helm-toolkit.manifests.job_bootstrap" }}
 {{- end }}
diff --git a/nova/templates/job-cell-setup.yaml b/nova/templates/job-cell-setup.yaml
index c0af822003..988ac165bf 100644
--- a/nova/templates/job-cell-setup.yaml
+++ b/nova/templates/job-cell-setup.yaml
@@ -67,6 +67,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+              readOnly: true
             - name: nova-etc
               mountPath: /etc/nova/policy.yaml
               subPath: policy.yaml
diff --git a/nova/templates/job-db-drop.yaml b/nova/templates/job-db-drop.yaml
index 48df2d40fd..3f07b1df64 100644
--- a/nova/templates/job-db-drop.yaml
+++ b/nova/templates/job-db-drop.yaml
@@ -16,9 +16,9 @@ limitations under the License.
 
 {{- if .Values.manifests.job_db_drop }}
 {{- $serviceName := "nova" -}}
-{{- $dbSvc := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "configDbSection" "database" "configDbKey" "connection" -}}
-{{- $dbApi := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "configDbSection" "api_database" "configDbKey" "connection" -}}
-{{- $dbCell := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "configDbSection" "cell0_database" "configDbKey" "connection" -}}
+{{- $dbSvc := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "logConfigFile" (printf "/etc/%s/logging.conf" $serviceName ) "configDbSection" "database" "configDbKey" "connection" -}}
+{{- $dbApi := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "logConfigFile" (printf "/etc/%s/logging.conf" $serviceName ) "configDbSection" "api_database" "configDbKey" "connection" -}}
+{{- $dbCell := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "logConfigFile" (printf "/etc/%s/logging.conf" $serviceName ) "configDbSection" "cell0_database" "configDbKey" "connection" -}}
 {{- $dbsToDrop := list $dbSvc $dbApi $dbCell }}
 {{- $dbDropJob := dict "envAll" . "serviceName" $serviceName "dbsToDrop" $dbsToDrop -}}
 {{ $dbDropJob | include "helm-toolkit.manifests.job_db_drop_mysql" }}
diff --git a/nova/templates/job-db-init.yaml b/nova/templates/job-db-init.yaml
index 0fd7783da5..1098f08c63 100644
--- a/nova/templates/job-db-init.yaml
+++ b/nova/templates/job-db-init.yaml
@@ -16,9 +16,9 @@ limitations under the License.
 
 {{- if .Values.manifests.job_db_init }}
 {{- $serviceName := "nova" -}}
-{{- $dbSvc := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "configDbSection" "database" "configDbKey" "connection" -}}
-{{- $dbApi := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "configDbSection" "api_database" "configDbKey" "connection" -}}
-{{- $dbCell := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "configDbSection" "cell0_database" "configDbKey" "connection" -}}
+{{- $dbSvc := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "logConfigFile" (printf "/etc/%s/logging.conf" $serviceName ) "configDbSection" "database" "configDbKey" "connection" -}}
+{{- $dbApi := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "logConfigFile" (printf "/etc/%s/logging.conf" $serviceName ) "configDbSection" "api_database" "configDbKey" "connection" -}}
+{{- $dbCell := dict "adminSecret" .Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "logConfigFile" (printf "/etc/%s/logging.conf" $serviceName ) "configDbSection" "cell0_database" "configDbKey" "connection" -}}
 {{- $dbsToInit := list $dbSvc $dbApi $dbCell }}
 {{- $dbInitJob := dict "envAll" . "serviceName" $serviceName "dbsToInit" $dbsToInit -}}
 {{ $dbInitJob | include "helm-toolkit.manifests.job_db_init_mysql" }}
diff --git a/nova/templates/statefulset-compute-ironic.yaml b/nova/templates/statefulset-compute-ironic.yaml
index 86f4f3f6b5..eca793938f 100644
--- a/nova/templates/statefulset-compute-ironic.yaml
+++ b/nova/templates/statefulset-compute-ironic.yaml
@@ -70,6 +70,10 @@ spec:
               mountPath: /etc/nova/nova.conf
               subPath: nova.conf
               readOnly: true
+            - name: nova-etc
+              mountPath: {{ .Values.conf.nova.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.nova.DEFAULT.log_config_append }}
+              readOnly: true
             - name: nova-etc
               mountPath: /etc/nova/nova-ironic.conf
               subPath: nova-ironic.conf
diff --git a/nova/values.yaml b/nova/values.yaml
index 97718fb49f..f45f227e83 100644
--- a/nova/values.yaml
+++ b/nova/values.yaml
@@ -1021,6 +1021,7 @@ conf:
       compute_driver: ironic.IronicDriver
   nova:
     DEFAULT:
+      log_config_append: /etc/nova/logging.conf
       default_ephemeral_format: ext4
       ram_allocation_ratio: 1.0
       disk_allocation_ratio: 1.0
@@ -1101,6 +1102,65 @@ conf:
     placement:
       auth_type: password
       auth_version: v3
+  logging:
+    loggers:
+      keys:
+        - root
+        - nova
+    handlers:
+      keys:
+        - stdout
+        - stderr
+        - "null"
+    formatters:
+      keys:
+        - context
+        - default
+    logger_root:
+      level: WARNING
+      handlers: null
+    logger_nova:
+      level: INFO
+      handlers:
+        - stdout
+        - stderr
+      qualname: nova
+    logger_amqp:
+      level: WARNING
+      handlers: stderr
+      qualname: amqp
+    logger_amqplib:
+      level: WARNING
+      handlers: stderr
+      qualname: amqplib
+    logger_eventletwsgi:
+      level: WARNING
+      handlers: stderr
+      qualname: eventlet.wsgi.server
+    logger_sqlalchemy:
+      level: WARNING
+      handlers: stderr
+      qualname: sqlalchemy
+    logger_boto:
+      level: WARNING
+      handlers: stderr
+      qualname: boto
+    handler_null:
+      class: logging.NullHandler
+      formatter: default
+      args: ()
+    handler_stdout:
+      class: StreamHandler
+      args: (sys.stdout,)
+      formatter: context
+    handler_stderr:
+      class: StreamHandler
+      args: (sys.stderr,)
+      formatter: context
+    formatter_context:
+      class: oslo_log.formatters.ContextFormatter
+    formatter_default:
+      format: "%(message)s"
 
 # Names of secrets used by bootstrap and environmental checks
 secrets:
@@ -1416,6 +1476,21 @@ endpoints:
       api:
         default: 6385
         public: 80
+  fluentd:
+    namespace: null
+    name: fluentd
+    hosts:
+      default: fluentd-logging
+    host_fqdn_override:
+      default: null
+    path:
+      default: null
+    scheme: 'http'
+    port:
+      service:
+        default: 24224
+      metrics:
+        default: 24220
 
 pod:
   user:
diff --git a/senlin/templates/configmap-etc.yaml b/senlin/templates/configmap-etc.yaml
index f2086d129f..6eb648bddf 100644
--- a/senlin/templates/configmap-etc.yaml
+++ b/senlin/templates/configmap-etc.yaml
@@ -83,6 +83,18 @@ limitations under the License.
 {{- $_ := set .Values.conf.senlin.authentication "service_username" .Values.endpoints.identity.auth.senlin.username -}}
 {{- end -}}
 
+{{- if and (empty .Values.conf.logging.handler_fluent) (has "fluent" .Values.conf.logging.handlers.keys) -}}
+{{- $fluentd_host := tuple "fluentd" "internal" $envAll | include "helm-toolkit.endpoints.hostname_namespaced_endpoint_lookup" }}
+{{- $fluentd_port := tuple "fluentd" "internal" "service" $envAll | include "helm-toolkit.endpoints.endpoint_port_lookup" }}
+{{- $fluent_args := printf "('openstack.senlin', '%s', %s)" $fluentd_host $fluentd_port }}
+{{- $handler_fluent := dict "class" "fluent.handler.FluentHandler" "formatter" "fluent" "args" $fluent_args -}}
+{{- $_ := set .Values.conf.logging "handler_fluent" $handler_fluent -}}
+{{- end -}}
+
+{{- if and (empty .Values.conf.logging.formatter_fluent) (has "fluent" .Values.conf.logging.formatters.keys) -}}
+{{- $formatter_fluent := dict "class" "oslo_log.formatters.FluentFormatter" -}}
+{{- $_ := set .Values.conf.logging "formatter_fluent" $formatter_fluent -}}
+{{- end -}}
 ---
 apiVersion: v1
 kind: ConfigMap
@@ -93,6 +105,8 @@ data:
 {{ toYaml .Values.conf.rally_tests.tests | indent 4 }}
   senlin.conf: |
 {{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.senlin | indent 4 }}
+  logging.conf: |
+{{ include "helm-toolkit.utils.to_oslo_conf" .Values.conf.logging | indent 4 }}
   api-paste.ini: |
 {{ include "helm-toolkit.utils.to_ini" .Values.conf.paste | indent 4 }}
   policy.json: |
diff --git a/senlin/templates/cron-job-engine-cleaner.yaml b/senlin/templates/cron-job-engine-cleaner.yaml
index 00169b139c..2ab3ea3fa7 100644
--- a/senlin/templates/cron-job-engine-cleaner.yaml
+++ b/senlin/templates/cron-job-engine-cleaner.yaml
@@ -65,6 +65,10 @@ spec:
                 mountPath: /etc/senlin/senlin.conf
                 subPath: senlin.conf
                 readOnly: true
+              - name: senlin-etc
+                mountPath: {{ .Values.conf.senlin.DEFAULT.log_config_append }}
+                subPath: {{ base .Values.conf.senlin.DEFAULT.log_config_append }}
+                readOnly: true
 {{ if $mounts_senlin_engine_cleaner.volumeMounts }}{{ toYaml $mounts_senlin_engine_cleaner.volumeMounts | indent 14 }}{{ end }}
           volumes:
           - name: etcsenlin
diff --git a/senlin/templates/deployment-api.yaml b/senlin/templates/deployment-api.yaml
index b3de86e7e8..437db345a5 100644
--- a/senlin/templates/deployment-api.yaml
+++ b/senlin/templates/deployment-api.yaml
@@ -89,6 +89,10 @@ spec:
               mountPath: /etc/senlin/senlin.conf
               subPath: senlin.conf
               readOnly: true
+            - name: senlin-etc
+              mountPath: {{ .Values.conf.senlin.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.senlin.DEFAULT.log_config_append }}
+              readOnly: true
             - name: senlin-etc
               mountPath: /etc/senlin/api-paste.ini
               subPath: api-paste.ini
diff --git a/senlin/templates/deployment-engine.yaml b/senlin/templates/deployment-engine.yaml
index 9ac0c569d5..c9b6487acd 100644
--- a/senlin/templates/deployment-engine.yaml
+++ b/senlin/templates/deployment-engine.yaml
@@ -68,6 +68,10 @@ spec:
               mountPath: /etc/senlin/senlin.conf
               subPath: senlin.conf
               readOnly: true
+            - name: senlin-etc
+              mountPath: {{ .Values.conf.senlin.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.senlin.DEFAULT.log_config_append }}
+              readOnly: true
             - name: senlin-etc
               mountPath: /etc/senlin/policy.json
               subPath: policy.json
diff --git a/senlin/templates/job-bootstrap.yaml b/senlin/templates/job-bootstrap.yaml
index 662dfa3138..9be8ad0629 100644
--- a/senlin/templates/job-bootstrap.yaml
+++ b/senlin/templates/job-bootstrap.yaml
@@ -15,6 +15,6 @@ limitations under the License.
 */}}
 
 {{- if and .Values.manifests.job_bootstrap .Values.bootstrap.enabled }}
-{{- $bootstrapJob := dict "envAll" . "serviceName" "senlin" "keystoneUser" .Values.bootstrap.ks_user -}}
+{{- $bootstrapJob := dict "envAll" . "serviceName" "senlin" "keystoneUser" .Values.bootstrap.ks_user "logConfigFile" .Values.conf.senlin.DEFAULT.log_config_append -}}
 {{ $bootstrapJob | include "helm-toolkit.manifests.job_bootstrap" }}
 {{- end }}
diff --git a/senlin/values.yaml b/senlin/values.yaml
index 53802f1d9f..73f451dc2a 100644
--- a/senlin/values.yaml
+++ b/senlin/values.yaml
@@ -165,6 +165,7 @@ conf:
     webhooks:trigger: ''
   senlin:
     DEFAULT:
+      log_config_append: /etc/senlin/logging.conf
       transport_url: null
       host: senlin
     database:
@@ -179,6 +180,65 @@ conf:
       # NOTE(portdirect): the bind port should not be defined, and is manipulated
       # via the endpoints section.
       bind_port: null
+  logging:
+    loggers:
+      keys:
+        - root
+        - senlin
+    handlers:
+      keys:
+        - stdout
+        - stderr
+        - "null"
+    formatters:
+      keys:
+        - context
+        - default
+    logger_root:
+      level: WARNING
+      handlers: null
+    logger_senlin:
+      level: INFO
+      handlers:
+        - stdout
+        - stderr
+      qualname: senlin
+    logger_amqp:
+      level: WARNING
+      handlers: stderr
+      qualname: amqp
+    logger_amqplib:
+      level: WARNING
+      handlers: stderr
+      qualname: amqplib
+    logger_eventletwsgi:
+      level: WARNING
+      handlers: stderr
+      qualname: eventlet.wsgi.server
+    logger_sqlalchemy:
+      level: WARNING
+      handlers: stderr
+      qualname: sqlalchemy
+    logger_boto:
+      level: WARNING
+      handlers: stderr
+      qualname: boto
+    handler_null:
+      class: logging.NullHandler
+      formatter: default
+      args: ()
+    handler_stdout:
+      class: StreamHandler
+      args: (sys.stdout,)
+      formatter: context
+    handler_stderr:
+      class: StreamHandler
+      args: (sys.stderr,)
+      formatter: context
+    formatter_context:
+      class: oslo_log.formatters.ContextFormatter
+    formatter_default:
+      format: "%(message)s"
 
 network:
   api:
@@ -286,7 +346,6 @@ dependencies:
         - endpoint: internal
           service: local_image_registry
 
-
 # Names of secrets used by bootstrap and environmental checks
 secrets:
   identity:
@@ -422,6 +481,21 @@ endpoints:
         default: 5672
       http:
         default: 15672
+  fluentd:
+    namespace: null
+    name: fluentd
+    hosts:
+      default: fluentd-logging
+    host_fqdn_override:
+      default: null
+    path:
+      default: null
+    scheme: 'http'
+    port:
+      service:
+        default: 24224
+      metrics:
+        default: 24220
 
 pod:
   user: