Merge lp:~stub/charms/precise/postgresql/syslog into lp:charms/postgresql

Proposed by Stuart Bishop
Status: Merged
Merged at revision: 88
Proposed branch: lp:~stub/charms/precise/postgresql/syslog
Merge into: lp:charms/postgresql
Prerequisite: lp:~stub/charms/precise/postgresql/bug-1278731-hot-standby-allowed-units
Diff against target: 271 lines (+147/-1)
7 files modified
README.md (+10/-0)
config.yaml (+1/-1)
hooks/hooks.py (+73/-0)
metadata.yaml (+2/-0)
templates/postgresql.conf.tmpl (+2/-0)
templates/rsyslog_forward.conf (+19/-0)
test.py (+40/-0)
To merge this branch: bzr merge lp:~stub/charms/precise/postgresql/syslog
Reviewer Review Type Date Requested Status
Marco Ceppi (community) Approve
Charles Butler (community) deployment + audit Approve
Review via email: mp+206176@code.launchpad.net

Description of the change

Ship PostgreSQL log files via syslog.

I'm not too familiar with other charms using syslog, in particular if I should be sending on port 10514 or 514, so I'd like a review from someone who has done work in this area.

The goal of this work is to connect PostgreSQL to reporting programs, and I'll be creating a pgBadger charm shortly that will consume the logs. For this use case, I also need to advertise the log_line_prefix that PostgreSQL uses to consumers, so they can decode the logs. I also added the programname required to tell logs from different units apart (IP address isn't reliable).

To post a comment you must log in.
175. By Stuart Bishop

Basic docs for syslog

Revision history for this message
Charles Butler (lazypower) wrote :

Stuart,

These modifications look fine to me. I've pulled them and deployed, setup a rather basic deployment test using rsyslog charm and the branch+prereq on HP-Cloud, AWS, and Local providers. All of which were properly forwarding the syslog. I haven't deployed the PGBadger service, however I see it as being ready in the queue, and I'll get to that next with additional commentary.

From what I've experienced in the past, forwarding over port 514 to your logging-nexus is perfectly acceptable. If you want to get into TLS wrapping your rsyslog forwarding, that's typically when you go to port 1999, so I'm a bit confused on the port 10514 dialogue above. Can you shed some light on why that was of a concern?

I'll be pushing this review to another charmer for an additional +1 review.

Thank you for the submission, keep up the excellent work!

review: Approve (deployment + audit)
Revision history for this message
Stuart Bishop (stub) wrote :

On 25 February 2014 02:27, Charles Butler <email address hidden> wrote:

> These modifications look fine to me. I've pulled them and deployed, setup a rather basic deployment test using rsyslog charm and the branch+prereq on HP-Cloud, AWS, and Local providers. All of which were properly forwarding the syslog. I haven't deployed the PGBadger service, however I see it as being ready in the queue, and I'll get to that next with additional commentary.
>
> >From what I've experienced in the past, forwarding over port 514 to your logging-nexus is perfectly acceptable. If you want to get into TLS wrapping your rsyslog forwarding, that's typically when you go to port 1999, so I'm a bit confused on the port 10514 dialogue above. Can you shed some light on why that was of a concern?

Its a concern as I'm cargo culting code from elsewhere, rather than
follow a specification. If there actually is a specification for the
syslog interface somewhere please let me know ;)

From what I can tell with the rsyslog and rsyslog-forarder charms, the
'open' port is 514 (TCP+UDP) but the 'closed' one used for syslog
forwarding is 10514 (TCP only). I also see nothing about TLS in those
charms, and nothing on port 1999.

--
Stuart Bishop <email address hidden>

Revision history for this message
Charles Butler (lazypower) wrote :

Ah, i reached out to my prior co-workers about the TLS security wrappers
and port 1999, that was a convention we coined. its not part of the spec.

Apparently getting transport security on our syslog forwarding constituted
partying like its 1999, hence the port chosen.

On Tue, Feb 25, 2014 at 12:42 AM, Stuart Bishop <<email address hidden>
> wrote:

> On 25 February 2014 02:27, Charles Butler <email address hidden>
> wrote:
>
> > These modifications look fine to me. I've pulled them and deployed,
> setup a rather basic deployment test using rsyslog charm and the
> branch+prereq on HP-Cloud, AWS, and Local providers. All of which were
> properly forwarding the syslog. I haven't deployed the PGBadger service,
> however I see it as being ready in the queue, and I'll get to that next
> with additional commentary.
> >
> > >From what I've experienced in the past, forwarding over port 514 to
> your logging-nexus is perfectly acceptable. If you want to get into TLS
> wrapping your rsyslog forwarding, that's typically when you go to port
> 1999, so I'm a bit confused on the port 10514 dialogue above. Can you shed
> some light on why that was of a concern?
>
> Its a concern as I'm cargo culting code from elsewhere, rather than
> follow a specification. If there actually is a specification for the
> syslog interface somewhere please let me know ;)
>
> >From what I can tell with the rsyslog and rsyslog-forarder charms, the
> 'open' port is 514 (TCP+UDP) but the 'closed' one used for syslog
> forwarding is 10514 (TCP only). I also see nothing about TLS in those
> charms, and nothing on port 1999.
>
>
> --
> Stuart Bishop <email address hidden>
>
>
> https://code.launchpad.net/~stub/charms/precise/postgresql/syslog/+merge/206176
> You are reviewing the proposed merge of
> lp:~stub/charms/precise/postgresql/syslog into lp:charms/postgresql.
>

Revision history for this message
Marco Ceppi (marcoceppi) wrote :

LGTM, +1

review: Approve
176. By Stuart Bishop

Merged bug-1278731-hot-standby-allowed-units into syslog.

177. By Stuart Bishop

Merged bug-1278731-hot-standby-allowed-units into syslog.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'README.md'
--- README.md 2014-02-05 12:29:09 +0000
+++ README.md 2014-02-13 13:49:35 +0000
@@ -130,6 +130,16 @@
130- `allowed-units`: space separated list of allowed clients (unit name). You130- `allowed-units`: space separated list of allowed clients (unit name). You
131 should check this to determine if you can connect to the database yet.131 should check this to determine if you can connect to the database yet.
132132
133## During syslog-relation-changed
134
135### the postgresql service provides:
136
137- `programname`: the syslog 'programname' identifying this unit's
138 PostgreSQL logs.
139- `log_line_prefix`: the 'log_line_prefix' setting for the PostgreSQL
140 service.
141
142
133## For replicated database support143## For replicated database support
134144
135A PostgreSQL service may contain multiple units (a single master, and145A PostgreSQL service may contain multiple units (a single master, and
136146
=== modified file 'config.yaml'
--- config.yaml 2014-01-23 11:41:03 +0000
+++ config.yaml 2014-02-13 13:49:35 +0000
@@ -90,7 +90,7 @@
90 type: boolean90 type: boolean
91 description: Log disconnections91 description: Log disconnections
92 log_line_prefix:92 log_line_prefix:
93 default: "%t "93 default: "%t [%p]: [%l-1] db=%d,user=%u "
94 type: string94 type: string
95 description: |95 description: |
96 special values:96 special values:
9797
=== modified file 'hooks/hooks.py'
--- hooks/hooks.py 2014-02-13 13:49:35 +0000
+++ hooks/hooks.py 2014-02-13 13:49:35 +0000
@@ -14,6 +14,7 @@
14import subprocess14import subprocess
15import sys15import sys
16from tempfile import NamedTemporaryFile16from tempfile import NamedTemporaryFile
17from textwrap import dedent
17import time18import time
18import yaml19import yaml
19from yaml.constructor import ConstructorError20from yaml.constructor import ConstructorError
@@ -482,6 +483,9 @@
482 config_file, pg_config,483 config_file, pg_config,
483 owner="postgres", group="postgres", perms=0600)484 owner="postgres", group="postgres", perms=0600)
484485
486 # Create or update files included from postgresql.conf.
487 configure_log_destination(os.path.dirname(config_file))
488
485 local_state['saved_config'] = config_data489 local_state['saved_config'] = config_data
486 local_state.save()490 local_state.save()
487491
@@ -999,6 +1003,11 @@
999 postgresql_restart()1003 postgresql_restart()
1000 postgresql_reload_or_restart()1004 postgresql_reload_or_restart()
10011005
1006 # In case the log_line_prefix has changed, inform syslog consumers.
1007 for relid in hookenv.relation_ids('syslog'):
1008 hookenv.relation_set(
1009 relid, log_line_prefix=hookenv.config('log_line_prefix'))
1010
10021011
1003@hooks.hook()1012@hooks.hook()
1004def install(run_pre=True):1013def install(run_pre=True):
@@ -2194,6 +2203,69 @@
2194 host.service_reload('nagios-nrpe-server')2203 host.service_reload('nagios-nrpe-server')
21952204
21962205
2206@hooks.hook()
2207def syslog_relation_changed():
2208 configure_log_destination(_get_postgresql_config_dir())
2209 postgresql_reload()
2210
2211 # We extend the syslog interface by exposing the log_line_prefix.
2212 # This is required so consumers of the PostgreSQL logs can decode
2213 # them. Consumers not smart enough to cope with arbitrary prefixes
2214 # can at a minimum abort if they detect it is set to something they
2215 # cannot support. Similarly, inform the consumer of the programname
2216 # we are using so they can tell one units log messages from another.
2217 hookenv.relation_set(
2218 log_line_prefix=hookenv.config('log_line_prefix'),
2219 programname=sanitize(hookenv.local_unit()))
2220
2221 template_path = "{0}/templates/rsyslog_forward.conf".format(
2222 hookenv.charm_dir())
2223 rsyslog_conf = Template(open(template_path).read()).render(
2224 local_unit=sanitize(hookenv.local_unit()),
2225 raw_local_unit=hookenv.local_unit(),
2226 raw_remote_unit=hookenv.remote_unit(),
2227 remote_addr=hookenv.relation_get('private-address'))
2228 host.write_file(rsyslog_conf_path(hookenv.remote_unit()), rsyslog_conf)
2229 run(['service', 'rsyslog', 'restart'])
2230
2231
2232@hooks.hook()
2233def syslog_relation_departed():
2234 configure_log_destination(_get_postgresql_config_dir())
2235 postgresql_reload()
2236 os.unlink(rsyslog_conf_path(hookenv.remote_unit()))
2237 run(['service', 'rsyslog', 'restart'])
2238
2239
2240def configure_log_destination(config_dir):
2241 """Set the log_destination PostgreSQL config flag appropriately"""
2242 # We currently support either 'standard' logs (the files in
2243 # /var/log/postgresql), or syslog + 'standard' logs. This should
2244 # grow more complex in the future, as the local logs will be
2245 # redundant if you are using syslog for log aggregation, and we
2246 # probably want to add csvlog in the future. Note that csvlog
2247 # requires switching from 'Debian' log redirection and rotation to
2248 # the PostgreSQL builtin facilities.
2249 logdest_conf_path = os.path.join(config_dir, 'juju_logdest.conf')
2250 logdest_conf = open(logdest_conf_path, 'w')
2251 if hookenv.relation_ids('syslog'):
2252 # For syslog, we change the ident from the default of 'postgres'
2253 # to the unit name to allow remote services to easily identify
2254 # and filter which unit messages are from. We don't use IP
2255 # address for this as it is not necessarily unique.
2256 logdest_conf.write(dedent("""\
2257 log_destination='stderr,syslog'
2258 syslog_ident={0}
2259 """).format(sanitize(hookenv.local_unit())))
2260 else:
2261 open(logdest_conf_path, 'w').write("log_destination='stderr'")
2262
2263
2264def rsyslog_conf_path(remote_unit):
2265 return '/etc/rsyslog.d/juju-{0}-{1}.conf'.format(
2266 sanitize(hookenv.local_unit()), sanitize(remote_unit))
2267
2268
2197def _get_postgresql_config_dir(config_data=None):2269def _get_postgresql_config_dir(config_data=None):
2198 """ Return the directory path of the postgresql configuration files. """2270 """ Return the directory path of the postgresql configuration files. """
2199 if config_data is None:2271 if config_data is None:
@@ -2202,6 +2274,7 @@
2202 cluster_name = config_data['cluster_name']2274 cluster_name = config_data['cluster_name']
2203 return os.path.join("/etc/postgresql", version, cluster_name)2275 return os.path.join("/etc/postgresql", version, cluster_name)
22042276
2277
2205###############################################################################2278###############################################################################
2206# Global variables2279# Global variables
2207###############################################################################2280###############################################################################
22082281
=== added symlink 'hooks/syslog-relation-changed'
=== target is u'hooks.py'
=== added symlink 'hooks/syslog-relation-departed'
=== target is u'hooks.py'
=== modified file 'metadata.yaml'
--- metadata.yaml 2013-10-08 22:44:28 +0000
+++ metadata.yaml 2014-02-13 13:49:35 +0000
@@ -26,6 +26,8 @@
26requires:26requires:
27 persistent-storage:27 persistent-storage:
28 interface: directory-path28 interface: directory-path
29 syslog:
30 interface: syslog
29peers:31peers:
30 replication:32 replication:
31 interface: pgreplication33 interface: pgreplication
3234
=== modified file 'templates/postgresql.conf.tmpl'
--- templates/postgresql.conf.tmpl 2014-01-30 16:44:33 +0000
+++ templates/postgresql.conf.tmpl 2014-02-13 13:49:35 +0000
@@ -88,6 +88,8 @@
88# ERROR REPORTING AND LOGGING88# ERROR REPORTING AND LOGGING
89#------------------------------------------------------------------------------89#------------------------------------------------------------------------------
9090
91include 'juju_logdest.conf'
92
91{% if log_min_duration_statement != "" -%}93{% if log_min_duration_statement != "" -%}
92log_min_duration_statement = {{log_min_duration_statement}}94log_min_duration_statement = {{log_min_duration_statement}}
93{% endif -%}95{% endif -%}
9496
=== added file 'templates/rsyslog_forward.conf'
--- templates/rsyslog_forward.conf 1970-01-01 00:00:00 +0000
+++ templates/rsyslog_forward.conf 2014-02-13 13:49:35 +0000
@@ -0,0 +1,19 @@
1##
2## Managed by juju
3##
4
5# This file is installed into /etc/rsyslog.d to forward the unit's
6# PostgreSQL logs via syslog to another service.
7
8# This configuration is pretty dumb, and was cargo culted from the
9# rsyslog-forwarder charm. The client really needs to inform the
10# server what sort of service it requires (eg. reliability), but that
11# involves extending and documenting the juju common syslog interface.
12
13$ActionQueueType LinkedList # use asynchronous processing
14$ActionQueueFileName @@service@@ # set file name, also enables disk mode
15$ActionResumeRetryCount -1 # infinite retries on insert failure
16$ActionQueueSaveOnShutdown on # save in-memory data if rsyslog shuts down
17
18# Forward all PostgreSQL unit {{raw_local_unit}} logs to {{raw_remote_unit}}
19if $programname == '{{local_unit}}' then @@{{remote_addr}}:10514
020
=== modified file 'test.py'
--- test.py 2014-02-13 13:49:35 +0000
+++ test.py 2014-02-13 13:49:35 +0000
@@ -14,6 +14,7 @@
14import subprocess14import subprocess
15import time15import time
16import unittest16import unittest
17import uuid
1718
18import fixtures19import fixtures
19import psycopg220import psycopg2
@@ -137,6 +138,10 @@
137 result = self.sql('SELECT TRUE')138 result = self.sql('SELECT TRUE')
138 self.assertEqual(result, [['t']])139 self.assertEqual(result, [['t']])
139140
141 # Confirm that the relation tears down without errors.
142 self.juju.do(['destroy-relation', 'postgresql:db', 'psql:db'])
143 self.juju.wait_until_ready()
144
140 def test_streaming_replication(self):145 def test_streaming_replication(self):
141 self.juju.deploy(146 self.juju.deploy(
142 TEST_CHARM, 'postgresql', num_units=2, config=self.pg_config)147 TEST_CHARM, 'postgresql', num_units=2, config=self.pg_config)
@@ -163,6 +168,11 @@
163 result = self.sql('SELECT TRUE', dbname='postgres')168 result = self.sql('SELECT TRUE', dbname='postgres')
164 self.assertEqual(result, [['t']])169 self.assertEqual(result, [['t']])
165170
171 # Confirm that the relation tears down without errors.
172 self.juju.do([
173 'destroy-relation', 'postgresql:db-admin', 'psql:db-admin'])
174 self.juju.wait_until_ready()
175
166 def is_master(self, postgres_unit, dbname=None):176 def is_master(self, postgres_unit, dbname=None):
167 is_master = self.sql(177 is_master = self.sql(
168 'SELECT NOT pg_is_in_recovery()',178 'SELECT NOT pg_is_in_recovery()',
@@ -425,6 +435,36 @@
425 ''')435 ''')
426 self.assertEqual(result, [['f', 'f', 'f']])436 self.assertEqual(result, [['f', 'f', 'f']])
427437
438 def test_syslog(self):
439 # Deploy 2 PostgreSQL units and 2 rsyslog units to ensure that
440 # log messages from every source reach every sink.
441 self.pg_config['log_min_duration_statement'] = 0 # Log all statements
442 self.juju.deploy(
443 TEST_CHARM, 'postgresql', num_units=2, config=self.pg_config)
444 self.juju.deploy(PSQL_CHARM, 'psql')
445 self.juju.do(['add-relation', 'postgresql:db', 'psql:db'])
446 self.juju.deploy('cs:rsyslog', 'rsyslog', num_units=2)
447 self.juju.do([
448 'add-relation', 'postgresql:syslog', 'rsyslog:aggregator'])
449 self.juju.wait_until_ready()
450
451 token = str(uuid.uuid1())
452
453 self.sql("SELECT 'master {}'".format(token), 'master')
454 self.sql("SELECT 'hot standby {}'".format(token), 'hot standby')
455 time.sleep(2)
456
457 for runit in ['rsyslog/0', 'rsyslog/1']:
458 cmd = ['juju', 'ssh', runit, 'tail -100 /var/log/syslog']
459 out = run(self, cmd)
460 self.failUnless('master {}'.format(token) in out)
461 self.failUnless('hot standby {}'.format(token) in out)
462
463 # Confirm that the relation tears down correctly.
464 self.juju.do([
465 'destroy-relation', 'postgresql:syslog', 'rsyslog:aggregator'])
466 self.juju.wait_until_ready()
467
428468
429class PG91Tests(469class PG91Tests(
430 PostgreSQLCharmBaseTestCase,470 PostgreSQLCharmBaseTestCase,

Subscribers

People subscribed via source and target branches