github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/utils/syslog/config.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package syslog 5 6 import ( 7 "bytes" 8 "fmt" 9 "io/ioutil" 10 "path/filepath" 11 "strings" 12 "text/template" 13 ) 14 15 // tagOffset represents the substring start value for the tag to return 16 // the logfileName value from the syslogtag. Substrings in syslog are 17 // indexed from 1, hence the + 1. 18 const tagOffset = len("juju-") + 1 19 20 // The rsyslog conf for state server nodes. 21 // Messages are gathered from other nodes and accumulated in an all-machines.log file. 22 // 23 // The apparmor profile is quite strict about where rsyslog can write files. 24 // Instead of poking with the profile, the local provider now logs to 25 // {{logDir}}-{{user}}-{{env name}}/all-machines.log, and a symlink is made 26 // in the local provider log dir to point to that file. By 27 // default rsyslog creates files with 0644, but in the ubuntu package, the 28 // setting is changed to 0640. Using a new action directive (new as in 29 // not-legacy), we can specify the file create mode so it doesn't use 30 // the default. 31 // 32 // I would dearly love to write the filtering action as follows to avoid setting 33 // and resetting the global $FileCreateMode, but alas, precise doesn't support it 34 // 35 // if $syslogtag startswith "juju{{namespace}}-" then 36 // action(type="omfile" 37 // File="{{logDir}}{{namespace}}/all-machines.log" 38 // Template="JujuLogFormat{{namespace}}" 39 // FileCreateMode="0644") 40 // & stop 41 // 42 // Instead we need to mess with the global FileCreateMode. We set it back 43 // to the ubuntu default after defining our rule. 44 const stateServerRsyslogTemplate = ` 45 $ModLoad imuxsock 46 $ModLoad imfile 47 48 # Messages received from remote rsyslog machines have messages prefixed with a space, 49 # so add one in for local messages too if needed. 50 $template JujuLogFormat{{namespace}},"%syslogtag:{{tagStart}}:$%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n" 51 52 $template LongTagForwardFormat,"<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%" 53 54 {{range $i, $stateServerIP := stateServerHosts}} 55 # start: Forwarding rule for {{$stateServerIP}} 56 $ActionQueueType LinkedList 57 $ActionQueueFileName {{logfileName}}{{namespace}}_{{$i}} 58 $ActionResumeRetryCount -1 59 $ActionQueueSaveOnShutdown on 60 $ActionQueueMaxDiskSpace 512M 61 $DefaultNetstreamDriver gtls 62 $DefaultNetstreamDriverCAFile {{tlsCACertPath}} 63 $ActionSendStreamDriverAuthMode anon 64 $ActionSendStreamDriverMode 1 # run driver in TLS-only mode 65 66 :syslogtag, startswith, "juju{{namespace}}-" @@{{$stateServerIP}}:{{portNumber}};LongTagForwardFormat 67 # end: Forwarding rule for {{$stateServerIP}} 68 {{end}} 69 :syslogtag, startswith, "juju{{namespace}}-" stop 70 71 $FileCreateMode 0600 72 73 # Maximum size for the log on this outchannel is 512MB 74 # The command to execute when an outchannel as reached its size limit cannot accept any arguments 75 # that is why we have created the helper script for executing logrotate. 76 $outchannel logRotation,{{logDir}}/all-machines.log,536870912,{{logrotateHelperPath}} 77 78 $RuleSet remote 79 $FileCreateMode 0600 80 :syslogtag, startswith, "juju{{namespace}}-" :omfile:$logRotation;JujuLogFormat{{namespace}} 81 :syslogtag, startswith, "juju{{namespace}}-" stop 82 $FileCreateMode 0600 83 84 $InputFilePersistStateInterval 50 85 $InputFilePollInterval 5 86 $InputFileName {{logfilePath}} 87 $InputFileTag juju{{namespace}}-{{logfileName}}: 88 $InputFileStateFile {{logfileName}}{{namespace}} 89 $InputRunFileMonitor 90 91 $ModLoad imtcp 92 $DefaultNetstreamDriver gtls 93 $DefaultNetstreamDriverCAFile {{tlsCACertPath}} 94 $DefaultNetstreamDriverCertFile {{tlsCertPath}} 95 $DefaultNetstreamDriverKeyFile {{tlsKeyPath}} 96 $InputTCPServerStreamDriverAuthMode anon 97 $InputTCPServerStreamDriverMode 1 # run driver in TLS-only mode 98 $InputTCPMaxSessions 10000 # default is 200, all agents connect to all rsyslog daemons 99 100 $InputTCPServerBindRuleset remote 101 $InputTCPServerRun {{portNumber}} 102 103 # switch back to default ruleset for further rules 104 $RuleSet RSYSLOG_DefaultRuleset 105 ` 106 107 // The rsyslog conf for non-state server nodes. 108 // Messages are forwarded to the state server node. 109 // 110 // Each forwarding rule must be repeated in full for every state server and 111 // each rule must use a unique ActionQueueFileName 112 // See: http://www.rsyslog.com/doc/rsyslog_reliable_forwarding.html 113 const nodeRsyslogTemplate = ` 114 $ModLoad imuxsock 115 $ModLoad imfile 116 117 $InputFilePersistStateInterval 50 118 $InputFilePollInterval 5 119 $InputFileName {{logfilePath}} 120 $InputFileTag juju{{namespace}}-{{logfileName}}: 121 $InputFileStateFile {{logfileName}}{{namespace}} 122 $InputRunFileMonitor 123 {{range $i, $stateServerIP := stateServerHosts}} 124 # start: Forwarding rule for {{$stateServerIP}} 125 $ActionQueueType LinkedList 126 $ActionQueueFileName {{logfileName}}{{namespace}}_{{$i}} 127 $ActionResumeRetryCount -1 128 $ActionQueueSaveOnShutdown on 129 $ActionQueueMaxDiskSpace 512M 130 $DefaultNetstreamDriver gtls 131 $DefaultNetstreamDriverCAFile {{tlsCACertPath}} 132 $ActionSendStreamDriverAuthMode anon 133 $ActionSendStreamDriverMode 1 # run driver in TLS-only mode 134 135 $template LongTagForwardFormat,"<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%" 136 :syslogtag, startswith, "juju{{namespace}}-" @@{{$stateServerIP}}:{{portNumber}};LongTagForwardFormat 137 # end: Forwarding rule for {{$stateServerIP}} 138 {{end}} 139 & ~ 140 ` 141 142 // The logrotate conf for state serve nodes. 143 // default size is 512MB, ensuring that the log + one rotation 144 // will never take up more than 1GB of space. 145 // 146 // The size of the logrotate configuration is low for 147 // a very specific reason, see config comment. 148 const logrotateConf = ` 149 {{.LogDir}}/all-machines.log { 150 # rsyslogd informs logrotate when to rotate. 151 # The size specified here must be less than or equal to the log size 152 # when rsyslogd informs logrotate, or logrotate will take no action. 153 # The size value is otherwise unimportant. 154 size 1K 155 # maximum of one old file 156 rotate 1 157 # counting old files starts at 1 rather than 0 158 start 1 159 # ensure new file is created with the correct permissions 160 create 600 161 # reload rsyslog after rotation so it will use the new file 162 postrotate 163 service rsyslog reload 164 endscript 165 } 166 ` 167 168 var logrotateConfTemplate = template.Must(template.New("logrotate.conf").Parse(logrotateConf)) 169 170 // The logrotate helper script for state server nodes. 171 // We specify a state file to ensure we have the proper permissions. 172 const logrotateHelper = ` 173 /usr/sbin/logrotate -s {{.LogDir}}/logrotate.state {{.LogrotateConfPath}} 174 ` 175 176 var logrotateHelperTemplate = template.Must(template.New("logrotate.run").Parse(logrotateHelper)) 177 178 // nodeRsyslogTemplateTLSHeader is prepended to 179 // nodeRsyslogTemplate if TLS is to be used. 180 const nodeRsyslogTemplateTLSHeader = ` 181 ` 182 183 const ( 184 defaultConfigDir = "/etc/rsyslog.d" 185 defaultCACertFileName = "ca-cert.pem" 186 defaultServerCertFileName = "rsyslog-cert.pem" 187 defaultServerKeyFileName = "rsyslog-key.pem" 188 defaultLogrotateConfFileName = "logrotate.conf" 189 defaultLogrotateHelperFileName = "logrotate.run" 190 ) 191 192 // SyslogConfigRenderer instances are used to generate a rsyslog conf file. 193 type SyslogConfigRenderer interface { 194 Render() ([]byte, error) 195 } 196 197 // SyslogConfig provides a means to configure and generate rsyslog conf files for 198 // the state server nodes and unit nodes. 199 // rsyslog is configured to tail the specified log file. 200 type SyslogConfig struct { 201 // the template representing the config file contents. 202 configTemplate string 203 // the directory where the config file is written. 204 ConfigDir string 205 // the config file name. 206 ConfigFileName string 207 // the name of the log file to tail. 208 LogFileName string 209 // the name of the logrotate configuration file. 210 LogrotateConfFileName string 211 // the name of the script that executes the logrotate command. 212 LogrotateHelperFileName string 213 // the addresses of the state server to which messages should be forwarded. 214 StateServerAddresses []string 215 // CA certificate file name. 216 CACertFileName string 217 // Server certificate file name. 218 ServerCertFileName string 219 // Server private key file name. 220 ServerKeyFileName string 221 // the port number for the listener 222 Port int 223 // the directory for the logfiles 224 LogDir string 225 // namespace is used when there are multiple environments on one machine 226 Namespace string 227 } 228 229 // NewForwardConfig creates a SyslogConfig instance used on unit nodes to forward log entries 230 // to the state server nodes. 231 func NewForwardConfig(logFile, logDir string, port int, namespace string, stateServerAddresses []string) *SyslogConfig { 232 conf := &SyslogConfig{ 233 configTemplate: nodeRsyslogTemplate, 234 StateServerAddresses: stateServerAddresses, 235 LogFileName: logFile, 236 Port: port, 237 LogDir: logDir, 238 } 239 if namespace != "" { 240 conf.Namespace = "-" + namespace 241 } 242 return conf 243 } 244 245 // NewAccumulateConfig creates a SyslogConfig instance used to accumulate log entries from the 246 // various unit nodes. 247 func NewAccumulateConfig(logFile, logDir string, port int, namespace string, stateServerAddresses []string) *SyslogConfig { 248 conf := &SyslogConfig{ 249 configTemplate: stateServerRsyslogTemplate, 250 LogFileName: logFile, 251 Port: port, 252 LogDir: logDir, 253 StateServerAddresses: stateServerAddresses, 254 } 255 if namespace != "" { 256 conf.Namespace = "-" + namespace 257 } 258 return conf 259 } 260 261 func either(a, b string) string { 262 if a != "" { 263 return a 264 } 265 return b 266 } 267 268 func (slConfig *SyslogConfig) ConfigFilePath() string { 269 dir := either(slConfig.ConfigDir, defaultConfigDir) 270 return filepath.Join(dir, slConfig.ConfigFileName) 271 } 272 273 func (slConfig *SyslogConfig) CACertPath() string { 274 filename := either(slConfig.CACertFileName, defaultCACertFileName) 275 return filepath.Join(slConfig.LogDir, filename) 276 } 277 278 func (slConfig *SyslogConfig) ServerCertPath() string { 279 filename := either(slConfig.ServerCertFileName, defaultServerCertFileName) 280 return filepath.Join(slConfig.LogDir, filename) 281 } 282 283 func (slConfig *SyslogConfig) ServerKeyPath() string { 284 filename := either(slConfig.ServerCertFileName, defaultServerKeyFileName) 285 return filepath.Join(slConfig.LogDir, filename) 286 } 287 288 // LogrotateConfPath returns the the the entire logrotate.conf path including filename. 289 func (slConfig *SyslogConfig) LogrotateConfPath() string { 290 filename := either(slConfig.LogrotateConfFileName, defaultLogrotateConfFileName) 291 return filepath.Join(slConfig.LogDir, filename) 292 } 293 294 // LogrotateHelperPath returns the entire logrotate.helper path including filename. 295 func (slConfig *SyslogConfig) LogrotateHelperPath() string { 296 filename := either(slConfig.LogrotateHelperFileName, defaultLogrotateHelperFileName) 297 return filepath.Join(slConfig.LogDir, filename) 298 } 299 300 // LogrotateConfFile returns a ready to write to disk byte array of the logrotate.conf file. 301 func (slConfig *SyslogConfig) LogrotateConfFile() ([]byte, error) { 302 return slConfig.logrotateRender(logrotateConfTemplate) 303 } 304 305 // LogrotateHelperFile returns a ready to write to disk byte array of the logrotate.helper file. 306 func (slConfig *SyslogConfig) LogrotateHelperFile() ([]byte, error) { 307 return slConfig.logrotateRender(logrotateHelperTemplate) 308 } 309 310 func (slConfig *SyslogConfig) logrotateRender(t *template.Template) ([]byte, error) { 311 var buffer bytes.Buffer 312 if err := t.Execute(&buffer, slConfig); err != nil { 313 return nil, err 314 } 315 return buffer.Bytes(), nil 316 } 317 318 // Render generates the rsyslog config. 319 func (slConfig *SyslogConfig) Render() ([]byte, error) { 320 var stateServerHosts = func() []string { 321 var hosts []string 322 for _, addr := range slConfig.StateServerAddresses { 323 parts := strings.Split(addr, ":") 324 hosts = append(hosts, parts[0]) 325 } 326 return hosts 327 } 328 329 var logFilePath = func() string { 330 return fmt.Sprintf("%s/%s.log", slConfig.LogDir, slConfig.LogFileName) 331 } 332 333 t := template.New("syslogConfig") 334 t.Funcs(template.FuncMap{ 335 "logfileName": func() string { return slConfig.LogFileName }, 336 "stateServerHosts": stateServerHosts, 337 "logfilePath": logFilePath, 338 "portNumber": func() int { return slConfig.Port }, 339 "logDir": func() string { return slConfig.LogDir }, 340 "namespace": func() string { return slConfig.Namespace }, 341 "tagStart": func() int { return tagOffset + len(slConfig.Namespace) }, 342 "tlsCACertPath": slConfig.CACertPath, 343 "tlsCertPath": slConfig.ServerCertPath, 344 "tlsKeyPath": slConfig.ServerKeyPath, 345 "logrotateHelperPath": slConfig.LogrotateHelperPath, 346 }) 347 348 // Process the rsyslog config template and echo to the conf file. 349 p, err := t.Parse(slConfig.configTemplate) 350 if err != nil { 351 return nil, err 352 } 353 var confBuf bytes.Buffer 354 if err := p.Execute(&confBuf, nil); err != nil { 355 return nil, err 356 } 357 return confBuf.Bytes(), nil 358 } 359 360 // Write generates and writes the rsyslog config. 361 func (slConfig *SyslogConfig) Write() error { 362 data, err := slConfig.Render() 363 if err != nil { 364 return err 365 } 366 err = ioutil.WriteFile(slConfig.ConfigFilePath(), data, 0644) 367 return err 368 }