Skip to content

Commit c88ac09

Browse files
tdeveliogluTaylan Develioglu
authored and
Taylan Develioglu
committed
Initial commit
0 parents  commit c88ac09

13 files changed

+767
-0
lines changed

.gitignore

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Binaries for programs and plugins
2+
*.exe
3+
*.dll
4+
*.so
5+
*.dylib
6+
7+
# Test binary, build with `go test -c`
8+
*.test
9+
10+
# Output of the go coverage tool, specifically when used with LiteIDE
11+
*.out
12+
13+
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
14+
.glide/
15+
16+
# Vim swap files
17+
.*.swp
18+
19+
/prometheus-nginx-log-exporter

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Taylan Develioglu
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# prometheus-nginx-log-exporter
2+
An nginx logs to prometheus exporter

conf/nginx-log-exporter.yaml.example

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
loglevel: DEBUG
3+
listen:
4+
address: 0.0.0.0
5+
port: 9900
6+
applications:
7+
gitlab:
8+
log_files:
9+
- "/var/log/gitlab/nginx/gitlab_access.log"
10+
format: $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent
11+
"$http_referer" "$http_user_agent" $request_time "$upstream_response_time"
12+
include:
13+
- path: "^/api/v4/"
14+
methods:
15+
- GET
16+
- POST
17+
exclude:
18+
- path: "^/api/v4/jobs/"
19+
methods:
20+
- POST
21+
replace:
22+
- path: "^/api/v4/users/[0-9]+/"
23+
with: "/api/v4/users/<id>/"
24+
gitlab-pages:
25+
log_files:
26+
- "/var/log/gitlab/nginx/gitlab_pages_access.log"
27+
format: $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent
28+
"$http_referer" "$http_user_agent" $request_time "$upstream_response_time"

config.go

+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io/ioutil"
7+
"os"
8+
"path/filepath"
9+
"regexp"
10+
"sort"
11+
12+
"gopkg.in/yaml.v2"
13+
)
14+
15+
// globalConfig is our top-level configuration object
16+
type globalConfig struct {
17+
AppDir string `app_dir` // directory with app configurations
18+
LogLevel string
19+
Listen httpConfig
20+
21+
Applications map[string]*appConfig
22+
}
23+
24+
func (gc *globalConfig) init() error {
25+
// Load application config files
26+
if gc.AppDir != "" {
27+
fileInfos, err := ioutil.ReadDir(filepath.Join(gc.AppDir))
28+
if err != nil {
29+
return err
30+
}
31+
32+
gc.Applications = map[string]*appConfig{}
33+
34+
for _, fi := range fileInfos {
35+
if filepath.Ext(fi.Name()) != ".yaml" || fi.IsDir() {
36+
continue
37+
}
38+
path := filepath.Join(gc.AppDir, fi.Name())
39+
40+
// Check whether symlink and if it points to a regular file
41+
if !fi.Mode().IsRegular() {
42+
fi2, err := os.Stat(path)
43+
if err != nil {
44+
return err
45+
}
46+
47+
if !fi2.Mode().IsRegular() {
48+
continue
49+
}
50+
}
51+
52+
yml, err := ioutil.ReadFile(path)
53+
if err != nil {
54+
return err
55+
}
56+
57+
ac := &appConfig{}
58+
if err = yaml.UnmarshalStrict(yml, ac); err != nil {
59+
return err
60+
}
61+
// "/foo/bar.yaml" -> "bar"
62+
appName := filepath.Base(path[:(len(path) - 5)])
63+
gc.Applications[appName] = ac
64+
}
65+
}
66+
67+
for name := range gc.Applications {
68+
if err := gc.Applications[name].init(); err != nil {
69+
return err
70+
}
71+
}
72+
return nil
73+
}
74+
75+
// httpConfig represents the configuration of the http server.
76+
type httpConfig struct {
77+
Address string
78+
Port int
79+
}
80+
81+
// appConfig represents a single nginx "application" to export log files for.
82+
type appConfig struct {
83+
Format string
84+
FromBeginning bool `from_beginning`
85+
Labels map[string]string
86+
LogFiles []string `log_files`
87+
88+
Exclude []filterConfig
89+
Include []filterConfig
90+
Replace []replaceConfig
91+
92+
orderedLabelNames []string
93+
orderedLabelValues []string
94+
}
95+
96+
// compileRegexes compiles the various regex strings in appConfig.
97+
func (ac *appConfig) compileRegexes() error {
98+
for i := range (*ac).Exclude {
99+
if err := (*ac).Exclude[i].compileRegex(); err != nil {
100+
return err
101+
}
102+
}
103+
104+
for i := range (*ac).Include {
105+
if err := (*ac).Include[i].compileRegex(); err != nil {
106+
return err
107+
}
108+
}
109+
110+
for i := range (*ac).Replace {
111+
if err := (*ac).Replace[i].compileRegex(); err != nil {
112+
return err
113+
}
114+
}
115+
return nil
116+
}
117+
118+
// init prepares an application config for use
119+
func (ac *appConfig) init() error {
120+
for _, lf := range ac.LogFiles {
121+
if !filepath.IsAbs(lf) {
122+
return errors.New(fmt.Sprintf("log file '%s': not an absolute path", lf))
123+
}
124+
}
125+
126+
keys := make([]string, len(ac.Labels))
127+
values := make([]string, len(ac.Labels))
128+
129+
for k := range ac.Labels {
130+
keys = append(keys, k)
131+
}
132+
133+
sort.Strings(keys)
134+
135+
for i, k := range keys {
136+
values[i] = ac.Labels[k]
137+
}
138+
139+
ac.orderedLabelNames = keys
140+
ac.orderedLabelValues = values
141+
142+
if err := ac.compileRegexes(); err != nil {
143+
return err
144+
}
145+
return nil
146+
}
147+
148+
// filterConfig represents an include or exclude filter for paths.
149+
type filterConfig struct {
150+
Path *string
151+
Methods []string
152+
153+
pathRe *regexp.Regexp
154+
}
155+
156+
// compileRegex compiles the path field regex of filterConfig.
157+
func (fc *filterConfig) compileRegex() error {
158+
var err error
159+
if fc.pathRe, err = regexp.Compile(*fc.Path); err != nil {
160+
return err
161+
}
162+
return nil
163+
}
164+
165+
// match checks if a method/path combination matches the filter.
166+
func (fc *filterConfig) match(method string, path string) bool {
167+
if !fc.pathRe.MatchString(path) {
168+
return false
169+
}
170+
171+
if len(fc.Methods) == 0 {
172+
return true
173+
}
174+
175+
for i := range fc.Methods {
176+
if method == fc.Methods[i] {
177+
return true
178+
}
179+
}
180+
return false
181+
}
182+
183+
// replaceConfig represents a replacement option for paths.
184+
type replaceConfig struct {
185+
filterConfig `,inline`
186+
187+
With string
188+
}
189+
190+
// replace performs replaceConfig's string replacement.
191+
func (rc *replaceConfig) replace(s string) string {
192+
return rc.pathRe.ReplaceAllString(s, rc.With)
193+
}
194+
195+
// newConfig creates a new global configuration from configuration file
196+
func newConfig(fileName string) (*globalConfig, error) {
197+
yml, err := ioutil.ReadFile(fileName)
198+
if err != nil {
199+
return nil, err
200+
}
201+
202+
gc := &globalConfig{
203+
LogLevel: "INFO",
204+
Listen: httpConfig{
205+
Address: "0.0.0.0",
206+
Port: 9900,
207+
},
208+
}
209+
if err = yaml.UnmarshalStrict(yml, gc); err != nil {
210+
return nil, err
211+
}
212+
213+
if err = gc.init(); err != nil {
214+
return nil, err
215+
}
216+
217+
return gc, nil
218+
}

errors.go

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package main
2+
3+
import "fmt"
4+
5+
type requestParseError string
6+
7+
func (e requestParseError) Error() string {
8+
return fmt.Sprintf("%s: invalid request", e)
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[Unit]
2+
Description=Prometheus exporter for Nginx logs
3+
After=network.target
4+
Requires=network.target
5+
6+
[Service]
7+
User=prometheus-nginx-log-exporter
8+
Group=prometheus-nginx-log-exporter
9+
EnvironmentFile=-/etc/sysconfig/prometheus-nginx-log-exporter
10+
ExecStart=/usr/sbin/prometheus-nginx-log-exporter -config-file /etc/prometheus-nginx-log-exporter/config.yaml
11+
Restart=always
12+
RestartSec=15s
13+
ProtectSystem=full
14+
15+
[Install]
16+
WantedBy=multi-user.target
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
%define booking_repo extras
2+
3+
%global debug_package %{nil}
4+
%global import_path github.com/tdevelioglu/prometheus-nginx-log-exporter
5+
%global gopath %{_datadir}/gocode
6+
7+
%global _extdir ext/redhat
8+
9+
Name: prometheus-nginx-log-exporter
10+
Version: 0.1.0
11+
Release: 1%{?dist}
12+
Summary: Prometheus exporter for Nginx logs
13+
License: MIT
14+
URL: http://%{import_path}
15+
Source0: https://%{import_path}/archive/%{version}/%{name}-%{version}.tar.gz
16+
BuildRequires: git
17+
BuildRequires: golang
18+
BuildRequires: systemd
19+
Requires: systemd
20+
21+
%description
22+
%{summary}
23+
24+
%prep
25+
%setup
26+
27+
%build
28+
mkdir _build
29+
pushd _build
30+
31+
mkdir -p src/$(dirname %{import_path})
32+
ln -s $(dirs +1 -l) src/%{import_path}
33+
export GOPATH=$(pwd):%{gopath}
34+
35+
go get %{import_path}
36+
go build -v -a %{import_path}
37+
38+
popd
39+
40+
%install
41+
install -Dp -m 0755 _build/%{name} %{buildroot}%{_sbindir}/%{name}
42+
install -Dp -m 0644 %{_extdir}/%{name}.service %{buildroot}%{_unitdir}/%{name}.service
43+
install -Dp -m 0644 %{_extdir}/%{name}.sysconfig %{buildroot}%{_sysconfdir}/sysconfig/%{name}
44+
install -Dp -m 0644 %{_extdir}/%{name}.yaml %{buildroot}%{_sysconfdir}/%{name}/config.yaml
45+
install -d -m 0755 %{buildroot}%{_sysconfdir}/%{name}/apps.d
46+
install -d -m 0711 %{buildroot}%{_var}/empty/%{name}
47+
48+
%clean
49+
rm -rf $RPM_BUILD_ROOT
50+
51+
%pre
52+
getent group %{name} >/dev/null || groupadd -r %{name} || :
53+
getent passwd %{name} >/dev/null || \
54+
useradd -c "Prometheus Nginx log exporter" -g %{name} \
55+
-s /sbin/nologin -r -d / %{name} 2> /dev/null || :
56+
57+
%post
58+
%systemd_post %{name}.service
59+
60+
%preun
61+
%systemd_preun %{name}.service
62+
63+
%postun
64+
%systemd_postun_with_restart %{name}.service
65+
66+
%files
67+
%defattr(-,root,root,-)
68+
%dir %attr(0711,root,root) %{_var}/empty/sshd
69+
%dir %{_sysconfdir}/%{name}/apps.d
70+
%{_sbindir}/%{name}
71+
%{_unitdir}/%{name}.service
72+
%config(noreplace) %{_sysconfdir}/sysconfig/%{name}
73+
%config(noreplace) %attr(0644, root, root) %{_sysconfdir}/%{name}/config.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Specify environment variables for prometheus-nginx-log-exporter here

0 commit comments

Comments
 (0)