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: