phihos-fluent-plugin-prometheus 2.0.3.pre.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/linux.yml +34 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +14 -0
  6. data/ChangeLog +43 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +202 -0
  9. data/README.md +537 -0
  10. data/Rakefile +7 -0
  11. data/fluent-plugin-prometheus.gemspec +22 -0
  12. data/lib/fluent/plugin/filter_prometheus.rb +50 -0
  13. data/lib/fluent/plugin/in_prometheus/async_wrapper.rb +47 -0
  14. data/lib/fluent/plugin/in_prometheus.rb +230 -0
  15. data/lib/fluent/plugin/in_prometheus_monitor.rb +107 -0
  16. data/lib/fluent/plugin/in_prometheus_output_monitor.rb +234 -0
  17. data/lib/fluent/plugin/in_prometheus_tail_monitor.rb +98 -0
  18. data/lib/fluent/plugin/out_prometheus.rb +49 -0
  19. data/lib/fluent/plugin/prometheus/data_store.rb +103 -0
  20. data/lib/fluent/plugin/prometheus/placeholder_expander.rb +132 -0
  21. data/lib/fluent/plugin/prometheus.rb +445 -0
  22. data/lib/fluent/plugin/prometheus_metrics.rb +77 -0
  23. data/misc/fluentd_sample.conf +170 -0
  24. data/misc/nginx_proxy.conf +22 -0
  25. data/misc/prometheus.yaml +13 -0
  26. data/misc/prometheus_alerts.yaml +59 -0
  27. data/spec/fluent/plugin/filter_prometheus_spec.rb +145 -0
  28. data/spec/fluent/plugin/in_prometheus_monitor_spec.rb +42 -0
  29. data/spec/fluent/plugin/in_prometheus_spec.rb +225 -0
  30. data/spec/fluent/plugin/in_prometheus_tail_monitor_spec.rb +42 -0
  31. data/spec/fluent/plugin/out_prometheus_spec.rb +166 -0
  32. data/spec/fluent/plugin/prometheus/placeholder_expander_spec.rb +110 -0
  33. data/spec/fluent/plugin/prometheus_metrics_spec.rb +138 -0
  34. data/spec/fluent/plugin/shared.rb +248 -0
  35. data/spec/spec_helper.rb +10 -0
  36. metadata +176 -0
@@ -0,0 +1,445 @@
1
+ require 'prometheus/client'
2
+ require 'prometheus/client/formats/text'
3
+ require 'fluent/plugin/prometheus/placeholder_expander'
4
+ require 'fluent/plugin/prometheus/data_store'
5
+
6
+ module Fluent
7
+ module Plugin
8
+ module PrometheusLabelParser
9
+ def configure(conf)
10
+ super
11
+ # Check if running with multiple workers
12
+ sysconf = if self.respond_to?(:owner) && owner.respond_to?(:system_config)
13
+ owner.system_config
14
+ elsif self.respond_to?(:system_config)
15
+ self.system_config
16
+ else
17
+ nil
18
+ end
19
+ @multi_worker = sysconf && sysconf.workers ? (sysconf.workers > 1) : false
20
+ end
21
+
22
+ def parse_labels_elements(conf)
23
+ base_labels = Fluent::Plugin::Prometheus.parse_labels_elements(conf)
24
+
25
+ if @multi_worker
26
+ base_labels[:worker_id] = fluentd_worker_id.to_s
27
+ end
28
+
29
+ base_labels
30
+ end
31
+ end
32
+
33
+ module Prometheus
34
+ class AlreadyRegisteredError < StandardError; end
35
+
36
+ def self.parse_labels_elements(conf)
37
+ labels = conf.elements.select { |e| e.name == 'labels' }
38
+ if labels.size > 1
39
+ raise ConfigError, "labels section must have at most 1"
40
+ end
41
+
42
+ base_labels = {}
43
+ unless labels.empty?
44
+ labels.first.each do |key, value|
45
+ labels.first.has_key?(key)
46
+
47
+ # use RecordAccessor only for $. and $[ syntax
48
+ # otherwise use the value as is or expand the value by RecordTransformer for ${} syntax
49
+ if value.start_with?('$.') || value.start_with?('$[')
50
+ base_labels[key.to_sym] = PluginHelper::RecordAccessor::Accessor.new(value)
51
+ else
52
+ base_labels[key.to_sym] = value
53
+ end
54
+ end
55
+ end
56
+
57
+ base_labels
58
+ end
59
+
60
+ def self.parse_metrics_elements(conf, registry, labels = {})
61
+ metrics = []
62
+ conf.elements.select { |element|
63
+ element.name == 'metric'
64
+ }.each { |element|
65
+ if element.has_key?('key') && (element['key'].start_with?('$.') || element['key'].start_with?('$['))
66
+ value = element['key']
67
+ element['key'] = PluginHelper::RecordAccessor::Accessor.new(value)
68
+ end
69
+ case element['type']
70
+ when 'summary'
71
+ metrics << Fluent::Plugin::Prometheus::Summary.new(element, registry, labels)
72
+ when 'gauge'
73
+ metrics << Fluent::Plugin::Prometheus::Gauge.new(element, registry, labels)
74
+ when 'counter'
75
+ metrics << Fluent::Plugin::Prometheus::Counter.new(element, registry, labels)
76
+ when 'histogram'
77
+ metrics << Fluent::Plugin::Prometheus::Histogram.new(element, registry, labels)
78
+ else
79
+ raise ConfigError, "type option must be 'counter', 'gauge', 'summary' or 'histogram'"
80
+ end
81
+ }
82
+ metrics
83
+ end
84
+
85
+ def self.start_retention_threads(metrics, registry, thread_create, thread_running, log)
86
+ metrics.select { |metric| metric.has_retention? }.each do |metric|
87
+ thread_create.call("prometheus_retention_#{metric.name}".to_sym) do
88
+ while thread_running.call()
89
+ metric.remove_expired_metrics(registry, log)
90
+ sleep(metric.retention_check_interval)
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ def self.start_reset_threads(metrics, registry, thread_create, thread_running, log)
97
+ metrics.select { |metric| metric.has_reset? }.each do |metric|
98
+ thread_create.call("prometheus_reset_#{metric.name}".to_sym) do
99
+ while thread_running.call()
100
+ sleep(metric.reset_after)
101
+ metric.reset_values(registry, log)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ def self.placeholder_expander(log)
108
+ Fluent::Plugin::Prometheus::ExpandBuilder.new(log: log)
109
+ end
110
+
111
+ def stringify_keys(hash_to_stringify)
112
+ # Adapted from: https://www.jvt.me/posts/2019/09/07/ruby-hash-keys-string-symbol/
113
+ hash_to_stringify.map do |k,v|
114
+ value_or_hash = if v.instance_of? Hash
115
+ stringify_keys(v)
116
+ else
117
+ v
118
+ end
119
+ [k.to_s, value_or_hash]
120
+ end.to_h
121
+ end
122
+
123
+ def initialize
124
+ super
125
+ ::Prometheus::Client.config.data_store = Fluent::Plugin::Prometheus::DataStore.new
126
+ end
127
+
128
+ def configure(conf)
129
+ super
130
+ @placeholder_values = {}
131
+ @placeholder_expander_builder = Fluent::Plugin::Prometheus.placeholder_expander(log)
132
+ @hostname = Socket.gethostname
133
+ end
134
+
135
+ def instrument_single(tag, time, record, metrics)
136
+ @placeholder_values[tag] ||= {
137
+ 'tag' => tag,
138
+ 'hostname' => @hostname,
139
+ 'worker_id' => fluentd_worker_id,
140
+ }
141
+
142
+ record = stringify_keys(record)
143
+ placeholders = record.merge(@placeholder_values[tag])
144
+ expander = @placeholder_expander_builder.build(placeholders)
145
+ metrics.each do |metric|
146
+ begin
147
+ metric.instrument(record, expander)
148
+ rescue => e
149
+ log.warn "prometheus: failed to instrument a metric.", error_class: e.class, error: e, tag: tag, name: metric.name
150
+ router.emit_error_event(tag, time, record, e)
151
+ end
152
+ end
153
+ end
154
+
155
+ def instrument(tag, es, metrics)
156
+ placeholder_values = {
157
+ 'tag' => tag,
158
+ 'hostname' => @hostname,
159
+ 'worker_id' => fluentd_worker_id,
160
+ }
161
+
162
+ es.each do |time, record|
163
+ record = stringify_keys(record)
164
+ placeholders = record.merge(placeholder_values)
165
+ expander = @placeholder_expander_builder.build(placeholders)
166
+ metrics.each do |metric|
167
+ begin
168
+ metric.instrument(record, expander)
169
+ rescue => e
170
+ log.warn "prometheus: failed to instrument a metric.", error_class: e.class, error: e, tag: tag, name: metric.name
171
+ router.emit_error_event(tag, time, record, e)
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ class Metric
178
+ attr_reader :type
179
+ attr_reader :name
180
+ attr_reader :key
181
+ attr_reader :desc
182
+ attr_reader :retention
183
+ attr_reader :retention_check_interval
184
+ attr_reader :reset_after
185
+
186
+ def initialize(element, registry, labels)
187
+ ['name', 'desc'].each do |key|
188
+ if element[key].nil?
189
+ raise ConfigError, "metric requires '#{key}' option"
190
+ end
191
+ end
192
+ @type = element['type']
193
+ @name = element['name']
194
+ @key = element['key']
195
+ @desc = element['desc']
196
+ @retention = element['retention'].to_i
197
+ @retention_check_interval = element.fetch('retention_check_interval', 60).to_i
198
+ if has_retention?
199
+ @last_modified_store = LastModifiedStore.new
200
+ end
201
+ @topk = element['topk'].to_i
202
+ @reset_after = element['reset_after'].to_i
203
+
204
+ @base_labels = Fluent::Plugin::Prometheus.parse_labels_elements(element)
205
+ @base_labels = labels.merge(@base_labels)
206
+ end
207
+
208
+ def labels(record, expander)
209
+ label = {}
210
+ @base_labels.each do |k, v|
211
+ if v.is_a?(String)
212
+ label[k] = expander.expand(v)
213
+ else
214
+ label[k] = v.call(record)
215
+ end
216
+ end
217
+ label
218
+ end
219
+
220
+ def self.get(registry, name, type, docstring)
221
+ metric = registry.get(name)
222
+
223
+ # should have same type, docstring
224
+ if metric.type != type
225
+ raise AlreadyRegisteredError, "#{name} has already been registered as #{type} type"
226
+ end
227
+ if metric.docstring != docstring
228
+ raise AlreadyRegisteredError, "#{name} has already been registered with different docstring"
229
+ end
230
+
231
+ metric
232
+ end
233
+
234
+ def set_value?(value)
235
+ if value
236
+ return true
237
+ end
238
+ false
239
+ end
240
+
241
+ def instrument(record, expander)
242
+ value = self.value(record)
243
+ if self.set_value?(value)
244
+ labels = labels(record, expander)
245
+ set_value(value, labels)
246
+ if has_retention?
247
+ @last_modified_store.set_last_updated(labels)
248
+ end
249
+ end
250
+ end
251
+
252
+ def has_retention?
253
+ @retention > 0
254
+ end
255
+
256
+ def has_reset?
257
+ reset_after > 0
258
+ end
259
+
260
+ def remove_expired_metrics(registry, log)
261
+ if has_retention?
262
+ metric = registry.get(@name)
263
+
264
+ expiration_time = Time.now - @retention
265
+ expired_label_sets = @last_modified_store.get_labels_not_modified_since(expiration_time)
266
+
267
+ expired_label_sets.each { |expired_label_set|
268
+ log.debug "Metric #{@name} with labels #{expired_label_set} expired. Removing..."
269
+ metric.remove(expired_label_set) # this method is supplied by the require at the top of this method
270
+ @last_modified_store.remove(expired_label_set)
271
+ }
272
+ else
273
+ log.warn('remove_expired_metrics should not be called when retention is not set for this metric!')
274
+ end
275
+ end
276
+
277
+ def reset_values(registry, log)
278
+ if has_reset?
279
+ metric = registry.get(@name)
280
+ log.debug "Resetting values nof metric #{@name}..."
281
+ metric.reset_values
282
+ else
283
+ log.warn('reset_store should not be called when reset_after is not set for this metric!')
284
+ end
285
+ end
286
+
287
+ class LastModifiedStore
288
+ def initialize
289
+ @internal_store = Hash.new
290
+ @lock = Monitor.new
291
+ end
292
+
293
+ def synchronize
294
+ @lock.synchronize { yield }
295
+ end
296
+
297
+ def set_last_updated(labels)
298
+ synchronize do
299
+ @internal_store[labels] = Time.now
300
+ end
301
+ end
302
+
303
+ def remove(labels)
304
+ synchronize do
305
+ @internal_store.delete(labels)
306
+ end
307
+ end
308
+
309
+ def get_labels_not_modified_since(time)
310
+ synchronize do
311
+ @internal_store.select { |k, v| v < time }.keys
312
+ end
313
+ end
314
+ end
315
+ end
316
+
317
+ class Gauge < Metric
318
+ def initialize(element, registry, labels)
319
+ super
320
+ if @key.nil?
321
+ raise ConfigError, "gauge metric requires 'key' option"
322
+ end
323
+
324
+ begin
325
+ @gauge = registry.gauge(
326
+ element['name'].to_sym,
327
+ docstring: element['desc'],
328
+ labels: @base_labels.keys,
329
+ store_settings: { topk: @topk }
330
+ )
331
+ rescue ::Prometheus::Client::Registry::AlreadyRegisteredError
332
+ @gauge = Fluent::Plugin::Prometheus::Metric.get(registry, element['name'].to_sym, :gauge, element['desc'])
333
+ end
334
+ end
335
+
336
+ def value(record)
337
+ if @key.is_a?(String)
338
+ record[@key]
339
+ else
340
+ @key.call(record)
341
+ end
342
+ end
343
+
344
+ def set_value(value, labels)
345
+ @gauge.set(value, labels: labels)
346
+ end
347
+ end
348
+
349
+ class Counter < Metric
350
+ def initialize(element, registry, labels)
351
+ super
352
+ begin
353
+ @counter = registry.counter(
354
+ element['name'].to_sym,
355
+ docstring: element['desc'],
356
+ labels: @base_labels.keys,
357
+ store_settings: { topk: @topk }
358
+ )
359
+ rescue ::Prometheus::Client::Registry::AlreadyRegisteredError
360
+ @counter = Fluent::Plugin::Prometheus::Metric.get(registry, element['name'].to_sym, :counter, element['desc'])
361
+ end
362
+ end
363
+
364
+ def value(record)
365
+ if @key.nil?
366
+ 1
367
+ elsif @key.is_a?(String)
368
+ record[@key]
369
+ else
370
+ @key.call(record)
371
+ end
372
+ end
373
+
374
+ def set_value?(value)
375
+ !value.nil?
376
+ end
377
+
378
+ def set_value(value, labels)
379
+ @counter.increment(by: value, labels: labels)
380
+ end
381
+ end
382
+
383
+ class Summary < Metric
384
+ def initialize(element, registry, labels)
385
+ super
386
+ if @key.nil?
387
+ raise ConfigError, "summary metric requires 'key' option"
388
+ end
389
+
390
+ begin
391
+ @summary = registry.summary(element['name'].to_sym, docstring: element['desc'], labels: @base_labels.keys)
392
+ rescue ::Prometheus::Client::Registry::AlreadyRegisteredError
393
+ @summary = Fluent::Plugin::Prometheus::Metric.get(registry, element['name'].to_sym, :summary, element['desc'])
394
+ end
395
+ end
396
+
397
+ def value(record)
398
+ if @key.is_a?(String)
399
+ record[@key]
400
+ else
401
+ @key.call(record)
402
+ end
403
+ end
404
+
405
+ def set_value(value, labels)
406
+ @summary.observe(value, labels: labels)
407
+ end
408
+ end
409
+
410
+ class Histogram < Metric
411
+ def initialize(element, registry, labels)
412
+ super
413
+ if @key.nil?
414
+ raise ConfigError, "histogram metric requires 'key' option"
415
+ end
416
+
417
+ begin
418
+ if element['buckets']
419
+ buckets = element['buckets'].split(/,/).map(&:strip).map do |e|
420
+ e[/\A\d+.\d+\Z/] ? e.to_f : e.to_i
421
+ end
422
+ @histogram = registry.histogram(element['name'].to_sym, docstring: element['desc'], labels: @base_labels.keys, buckets: buckets)
423
+ else
424
+ @histogram = registry.histogram(element['name'].to_sym, docstring: element['desc'], labels: @base_labels.keys)
425
+ end
426
+ rescue ::Prometheus::Client::Registry::AlreadyRegisteredError
427
+ @histogram = Fluent::Plugin::Prometheus::Metric.get(registry, element['name'].to_sym, :histogram, element['desc'])
428
+ end
429
+ end
430
+
431
+ def value(record)
432
+ if @key.is_a?(String)
433
+ record[@key]
434
+ else
435
+ @key.call(record)
436
+ end
437
+ end
438
+
439
+ def set_value(value, labels)
440
+ @histogram.observe(value, labels: labels)
441
+ end
442
+ end
443
+ end
444
+ end
445
+ end
@@ -0,0 +1,77 @@
1
+ module Fluent::Plugin
2
+
3
+ ##
4
+ # PromMetricsAggregator aggregates multiples metrics exposed using Prometheus text-based format
5
+ # see https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md
6
+
7
+
8
+ class PrometheusMetrics
9
+ def initialize
10
+ @comments = []
11
+ @metrics = []
12
+ end
13
+
14
+ def to_string
15
+ (@comments + @metrics).join("\n")
16
+ end
17
+
18
+ def add_comment(comment)
19
+ @comments << comment
20
+ end
21
+
22
+ def add_metric_value(value)
23
+ @metrics << value
24
+ end
25
+
26
+ attr_writer :comments, :metrics
27
+ end
28
+
29
+ class PromMetricsAggregator
30
+ def initialize
31
+ @metrics = {}
32
+ end
33
+
34
+ def get_metric_name_from_comment(line)
35
+ tokens = line.split(' ')
36
+ if ['HELP', 'TYPE'].include?(tokens[1])
37
+ tokens[2]
38
+ else
39
+ ''
40
+ end
41
+ end
42
+
43
+ def add_metrics(metrics)
44
+ current_metric = ''
45
+ new_metric = false
46
+ lines = metrics.split("\n")
47
+ for line in lines
48
+ if line[0] == '#'
49
+ # Metric comment (# TYPE, # HELP)
50
+ parsed_metric = get_metric_name_from_comment(line)
51
+ if parsed_metric != ''
52
+ if parsed_metric != current_metric
53
+ # Starting a new metric comment block
54
+ new_metric = !@metrics.key?(parsed_metric)
55
+ if new_metric
56
+ @metrics[parsed_metric] = PrometheusMetrics.new()
57
+ end
58
+ current_metric = parsed_metric
59
+ end
60
+
61
+ if new_metric && parsed_metric == current_metric
62
+ # New metric, inject comments (# TYPE, # HELP)
63
+ @metrics[parsed_metric].add_comment(line)
64
+ end
65
+ end
66
+ else
67
+ # Metric value, simply append line
68
+ @metrics[current_metric].add_metric_value(line)
69
+ end
70
+ end
71
+ end
72
+
73
+ def get_metrics
74
+ @metrics.map{|k,v| v.to_string()}.join("\n") + (@metrics.length ? "\n" : "")
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,170 @@
1
+ ## Prometheus Input Plugin Configuration
2
+
3
+ # input plugin that exports metrics
4
+ <source>
5
+ @type prometheus
6
+ </source>
7
+
8
+ <source>
9
+ @type monitor_agent
10
+ </source>
11
+
12
+ <source>
13
+ @type forward
14
+ </source>
15
+
16
+ # input plugin that collects metrics from MonitorAgent
17
+ <source>
18
+ @type prometheus_monitor
19
+ <labels>
20
+ host ${hostname}
21
+ </labels>
22
+ </source>
23
+
24
+ # input plugin that collects metrics for output plugin
25
+ <source>
26
+ @type prometheus_output_monitor
27
+ <labels>
28
+ host ${hostname}
29
+ </labels>
30
+ </source>
31
+
32
+ # input plugin that collects metrics for in_tail plugin
33
+ <source>
34
+ @type prometheus_tail_monitor
35
+ <labels>
36
+ host ${hostname}
37
+ </labels>
38
+ </source>
39
+
40
+ ## Nginx Access Log Configuration
41
+
42
+ <source>
43
+ @type tail
44
+ format nginx
45
+ tag nginx
46
+ path /var/log/nginx/access.log
47
+ pos_file /tmp/fluent_nginx.pos
48
+ types size:integer
49
+ </source>
50
+
51
+ <filter nginx>
52
+ @type prometheus
53
+
54
+ # You can use counter type with specifying a key,
55
+ # and increments counter by the value
56
+ <metric>
57
+ name nginx_size_counter_bytes
58
+ type counter
59
+ desc nginx bytes sent
60
+ key size
61
+ <labels>
62
+ host ${hostname}
63
+ foo bar
64
+ </labels>
65
+ </metric>
66
+
67
+ # You can use counter type without specifying a key
68
+ # This just increments counter by 1
69
+ <metric>
70
+ name nginx_record_counts
71
+ type counter
72
+ desc the number of emited records
73
+ <labels>
74
+ host ${hostname}
75
+ </labels>
76
+ </metric>
77
+ </filter>
78
+
79
+ <match nginx>
80
+ @type copy
81
+ # for MonitorAgent sample
82
+ <store>
83
+ @id test_forward
84
+ @type forward
85
+ buffer_type memory
86
+ flush_interval 1s
87
+ max_retry_wait 2s
88
+ <buffer>
89
+ # max_retry_wait 10s
90
+ flush_interval 1s
91
+ # retry_type periodic
92
+ disable_retry_limit
93
+ </buffer>
94
+ # retry_limit 3
95
+ disable_retry_limit
96
+ <server>
97
+ host 127.0.0.1
98
+ port 20000
99
+ </server>
100
+ </store>
101
+ <store>
102
+ @type stdout
103
+ </store>
104
+ </match>
105
+
106
+ ## Nginx Proxy Log Configuration
107
+
108
+ <source>
109
+ @type tail
110
+ format ltsv
111
+ tag nginx_proxy
112
+ path /var/log/nginx/access_proxy.log
113
+ pos_file /tmp/fluent_nginx_proxy.pos
114
+ types size:integer,request_length:integer,bytes_sent:integer,body_bytes_sent:integer,request_time:float,upstream_response_time:float
115
+ </source>
116
+
117
+ <filter nginx_proxy>
118
+ @type prometheus
119
+
120
+ # common labels for all metrics
121
+ <labels>
122
+ host ${hostname}
123
+ method ${request_method}
124
+ status ${status}
125
+ </labels>
126
+
127
+ <metric>
128
+ name nginx_proxy_request_length_total_bytes
129
+ type counter
130
+ desc nginx proxy request length bytes
131
+ key request_length
132
+ </metric>
133
+ <metric>
134
+ name nginx_proxy_bytes_sent_total_bytes
135
+ type counter
136
+ desc nginx proxy bytes sent
137
+ key bytes_sent
138
+ </metric>
139
+ <metric>
140
+ name nginx_proxy_request_duration_total_milliseconds
141
+ type counter
142
+ desc nginx proxy request time
143
+ key request_time
144
+ </metric>
145
+ <metric>
146
+ name nginx_proxy_upstream_response_duration_total_milliseconds
147
+ type counter
148
+ desc nginx proxy upstream response time
149
+ key upstream_response_time
150
+ </metric>
151
+ <metric>
152
+ name nginx_proxy_request_duration_milliseconds
153
+ type summary
154
+ desc nginx proxy request duration summary
155
+ key request_time
156
+ </metric>
157
+ <metric>
158
+ name nginx_proxy_upstream_duration_milliseconds
159
+ type summary
160
+ desc nginx proxy upstream response duration summary
161
+ key upstream_response_time
162
+ </metric>
163
+ </filter>
164
+
165
+ <match nginx_proxy>
166
+ @type copy
167
+ <store>
168
+ @type stdout
169
+ </store>
170
+ </match>
@@ -0,0 +1,22 @@
1
+ log_format ltsv 'time:$time_iso8601\t'
2
+ 'remote_addr:$remote_addr\t'
3
+ 'request_method:$request_method\t'
4
+ 'request_length:$request_length\t'
5
+ 'request_uri:$request_uri\t'
6
+ 'uri:$uri\t'
7
+ 'status:$status\t'
8
+ 'bytes_sent:$bytes_sent\t'
9
+ 'body_bytes_sent:$body_bytes_sent\t'
10
+ 'referer:$http_referer\t'
11
+ 'useragent:$http_user_agent\t'
12
+ 'request_time:$request_time\t'
13
+ 'upstream_response_time:$upstream_response_time';
14
+
15
+ server {
16
+ access_log /var/log/nginx/access_proxy.log ltsv;
17
+ listen 9999;
18
+ location / {
19
+ proxy_pass https://www.google.com;
20
+ }
21
+ }
22
+