Manage Keystone logs for MOS 7.0

This commit:

- separates the logstreamer for keystone-all.log between
  MOS 6.1 and MOS 7.0 because in MOS 7.0 the HTTP response time is
  reported in microseconds instead of seconds.

- creates a new logstreamer and decoder to parse logs produced by keystone
  "main" and "admin".

Change-Id: Icb427d859b92a7365883b068364a3b269eda9be5
(cherry picked from commit fbcebd03d0)
This commit is contained in:
Guillaume Thouvenin 2015-09-09 09:47:50 +02:00
parent 9baaee9601
commit 51eaadade6
4 changed files with 293 additions and 1 deletions

View File

@ -49,7 +49,16 @@ class { 'lma_collector::notifications::controller':
}
# OpenStack logs are always useful for indexation and metrics collection
class { 'lma_collector::logs::openstack': }
if hiera('fuel_version') == '7.0' {
# With MOS 7.0, logs from keystone "main" and "admin" applications are not
# copied in keystone-all.log. So we need to add a specific logger for them.
# Also the format of keystone-all.log is different so we need to add a
# specific treatment for it.
class { 'lma_collector::logs::openstack_7_0': }
} else {
class { 'lma_collector::logs::openstack': }
}
if ! $storage_options['objects_ceph'] {
class { 'lma_collector::logs::swift': }

View File

@ -0,0 +1,112 @@
-- Copyright 2015 Mirantis, Inc.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
require "string"
local l = require 'lpeg'
l.locale(l)
local syslog = require "syslog"
local patt = require 'patterns'
local utils = require 'lma_utils'
local msg = {
Timestamp = nil,
Type = 'log',
Hostname = nil,
Payload = nil,
Pid = nil,
Fields = nil,
Severity = nil,
}
local pgname = {
keystone_wsgi_admin_access = 'keystone-wsgi-admin',
keystone_wsgi_main_access = 'keystone-wsgi-main',
}
local syslog_pattern = read_config("syslog_pattern") or error("syslog_pattern configuration must be specified")
local syslog_grammar = syslog.build_rsyslog_grammar(syslog_pattern)
local sp = l.space
local dot = l.P'.'
local quote = l.P'"'
local timestamp = l.Cg(patt.Timestamp, "Timestamp")
local pid = l.Cg(patt.Pid, "Pid")
local severity = l.Cg(patt.SeverityLabel, "SeverityLabel")
local message = l.Cg(patt.Message, "Message")
local openstack_grammar = l.Ct(timestamp * sp * pid * sp * severity * sp * message)
-- Grammar for parsing HTTP response attributes from OpenStack logs
local http_method = l.Cg(l.R"AZ"^3, "http_method")
local url = l.Cg( (1 - sp)^1, "http_url")
local http_version = l.Cg(l.digit * dot * l.digit, "http_version")
-- Nova changes the default log format of eventlet.wsgi (see nova/wsgi.py) and
-- prefixes the HTTP status, response size and response time values with
-- respectively "status: ", "len: " and "time: ".
-- Other OpenStack services just rely on the default log format.
-- TODO(pasquier-s): build the LPEG grammar based on the log_format parameter
-- passed to eventlet.wsgi.server similar to what the build_rsyslog_grammar
-- function does for RSyslog.
local http_status = l.P"status: "^-1 * l.Cg(l.digit^3, "http_status")
local response_size = l.P"len: "^-1 * l.Cg(l.digit^1 / tonumber, "http_response_size")
local response_time = l.P"time: "^-1 * l.Cg(l.digit^1 * dot^0 * l.digit^0 / tonumber, "http_response_time")
local http_grammar = patt.anywhere(l.Ct(
quote * http_method * sp * url * sp * l.P'HTTP/' * http_version * quote *
sp * http_status * sp * response_size * sp * response_time
))
local ip_address_grammar = patt.anywhere(l.Ct(
l.Cg(l.digit^-3 * dot * l.digit^-3 * dot * l.digit^-3 * dot * l.digit^-3, "ip_address")
))
function process_message ()
local log = read_message("Payload")
local m
msg.Fields = {}
if utils.parse_syslog_message(syslog_grammar, log, msg) then
m = openstack_grammar:match(msg.Payload)
if m then
if m.Pid then msg.Pid = m.Pid end
if m.Timestamp then msg.Timestamp = m.Timestamp end
msg.Payload = m.Message
msg.Fields.severity_label = m.SeverityLabel
end
else
return -1
end
m = http_grammar:match(msg.Payload)
if m then
msg.Fields.http_method = m.http_method
msg.Fields.http_status = m.http_status
msg.Fields.http_url = m.http_url
msg.Fields.http_version = m.http_version
msg.Fields.http_response_size = m.http_response_size
-- Since keystone is using Apache server the response time are
-- in microseconds. We convert them into seconds.
msg.Fields.http_response_time = m.http_response_time / 1e6
m = ip_address_grammar:match(msg.Payload)
if m then
msg.Fields.http_client_ip_address = m.ip_address
end
end
-- Make programname consistent
msg.Fields.programname = pgname[msg.Fields.programname] or msg.Fields.programname
inject_message(msg)
return 0
end

View File

@ -0,0 +1,75 @@
-- Copyright 2015 Mirantis, Inc.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
require "string"
local l = require 'lpeg'
l.locale(l)
local patt = require 'patterns'
local syslog = require 'syslog'
local utils = require 'lma_utils'
local msg = {
Timestamp = nil,
Type = 'log',
Hostname = nil,
Payload = nil,
Pid = nil,
Fields = nil,
Severity = nil,
}
local syslog_pattern = read_config("syslog_pattern") or error("syslog_pattern configuration must be specified")
local syslog_grammar = syslog.build_rsyslog_grammar(syslog_pattern)
local timestamp = l.Cg(patt.Timestamp, "Timestamp")
local sp = l.space
local pid = l.Cg(patt.Pid, "Pid")
local severity = l.Cg(patt.SeverityLabel, "SeverityLabel")
local message = l.Cg(patt.Message, "Message")
local openstack_grammar = l.Ct(timestamp * sp * pid * sp * severity * sp * message)
-- This grammar is intended for log messages that are generated before RSYSLOG
-- is fully configured
local fallback_syslog_pattern = read_config("fallback_syslog_pattern")
local fallback_syslog_grammar
if fallback_syslog_pattern then
fallback_syslog_grammar = syslog.build_rsyslog_grammar(fallback_syslog_pattern)
end
function process_message ()
local log = read_message("Payload")
if utils.parse_syslog_message(syslog_grammar, log, msg) or
(fallback_syslog_grammar and utils.parse_syslog_message(fallback_syslog_grammar, log, msg)) then
-- We are only interested by program named "main" and "admin"
if (msg.Fields.programname == "main" or msg.Fields.programname == "admin") then
msg.Fields.programname = "keystone-" .. msg.Fields.programname
local m = openstack_grammar:match(msg.Payload)
if m then
if m.Pid then msg.Pid = m.Pid end
if m.Timestamp then msg.Timestamp = m.Timestamp end
msg.Payload = m.Message
msg.Fields.severity_label = m.SeverityLabel
end
inject_message(msg)
end
return 0
end
return -1
end

View File

@ -0,0 +1,96 @@
# Copyright 2015 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
class lma_collector::logs::openstack_7_0 {
include lma_collector::params
include lma_collector::service
heka::decoder::sandbox { 'openstack':
config_dir => $lma_collector::params::config_dir,
filename => "${lma_collector::params::plugins_dir}/decoders/openstack_log.lua" ,
config => {
syslog_pattern => $lma_collector::params::syslog_pattern
},
notify => Class['lma_collector::service'],
}
heka::decoder::sandbox { 'keystone_7_0':
config_dir => $lma_collector::params::config_dir,
filename => "${lma_collector::params::plugins_dir}/decoders/keystone_7_0_log.lua" ,
config => {
syslog_pattern => $lma_collector::params::syslog_pattern
},
notify => Class['lma_collector::service'],
}
heka::decoder::sandbox { 'keystone_wsgi':
config_dir => $lma_collector::params::config_dir,
filename => "${lma_collector::params::plugins_dir}/decoders/keystone_wsgi_log.lua" ,
config => {
syslog_pattern => $lma_collector::params::syslog_pattern
},
notify => Class['lma_collector::service'],
}
# Use the <PRI> token as the delimiter because OpenStack services may log
# messages with newlines and the configuration of the Syslog daemon doesn't
# escape them.
heka::splitter::regex { 'openstack':
config_dir => $lma_collector::params::config_dir,
delimiter => '(<[0-9]+>)',
delimiter_eol => false,
notify => Class['lma_collector::service'],
}
heka::input::logstreamer { 'openstack_7_0':
config_dir => $lma_collector::params::config_dir,
decoder => 'openstack',
splitter => 'openstack',
file_match => '(?P<Service>nova|cinder|glance|heat|neutron|murano)-all\.log$',
differentiator => '[ \'openstack.\', \'Service\' ]',
require => [Heka::Decoder::Sandbox['openstack'], Heka::Splitter::Regex['openstack']],
notify => Class['lma_collector::service'],
}
heka::input::logstreamer { 'keystone_7_0':
config_dir => $lma_collector::params::config_dir,
decoder => 'keystone_7_0',
splitter => 'openstack',
file_match => 'keystone-all\.log$',
differentiator => '[ \'openstack.keystone\' ]',
require => [Heka::Decoder::Sandbox['keystone_7_0'], Heka::Splitter::Regex['openstack']],
notify => Class['lma_collector::service'],
}
heka::input::logstreamer { 'openstack_dashboard':
config_dir => $lma_collector::params::config_dir,
decoder => 'openstack',
file_match => 'dashboard\.log$',
differentiator => '[ \'openstack.horizon\' ]',
require => Heka::Decoder::Sandbox['openstack'],
notify => Class['lma_collector::service'],
}
heka::input::logstreamer { 'keystone_wsgi':
config_dir => $lma_collector::params::config_dir,
decoder => 'keystone_wsgi',
splitter => 'openstack',
# We cannot use /var/log/keystone because it is owned by the keystone user.
log_directory => '/var/log/',
file_match => 'user\.log$',
differentiator => '[ \'openstack.keystone\' ]',
require => [Heka::Decoder::Sandbox['keystone_wsgi'], Heka::Splitter::Regex['openstack']],
notify => Class['lma_collector::service'],
}
}