postfix-exporter 0.1.0
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.
- checksums.yaml +7 -0
 - data/.gitignore +5 -0
 - data/README.md +50 -0
 - data/bin/postfix-exporter +183 -0
 - data/postfix-exporter.gemspec +32 -0
 - metadata +123 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 4b3403736d6ac37fd2733f6db98f566a36e426de634fe971b96a332dd13527dc
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: b20157262dd52237db592744c80713c260ea6eb224e2feb5eac7a60d5665ca40
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 8d99493844dfb43b7b15a3742430f50db2c70ec3506b0c1bd93557a4ca4aca45d22c4eca5a95010571eac486519740858e415f9bae2329ff2a630962056bd10d
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: a10120c7f4b0b7a31f36b4fd215bb44ea14868e8281491c473e26812e8b2cfbad0352c87666437087875fb3e095c4036e8b0316461ae82b0077567e938040846
         
     | 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,50 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            This is a simple exporter for various statistics relating to a Postfix
         
     | 
| 
      
 2 
     | 
    
         
            +
            server.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # Features
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            * Examines mail queue periodically and exports `postfix_mail_queue_size`;
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            * Reads syslog entries as they happen, and exports disposition status
         
     | 
| 
      
 10 
     | 
    
         
            +
              counters (`postfix_delivery_delays_count`) per DSN, as well as delay summaries per
         
     | 
| 
      
 11 
     | 
    
         
            +
              DSN (`postfix_delivery_delays{quantile="..."}`);
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            * Total number of SMTP connections (`postfix_smtpd_connections_total`) and
         
     | 
| 
      
 14 
     | 
    
         
            +
              currently-active connections (`postfix_smtpd_active_connections`);
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            * Count how many delivery attempts were received
         
     | 
| 
      
 17 
     | 
    
         
            +
              (`postfix_incoming_delivery_attempts_total`), split out by whether we
         
     | 
| 
      
 18 
     | 
    
         
            +
              accepted or rejected the message (`status`) and the exact DSN provided to
         
     | 
| 
      
 19 
     | 
    
         
            +
              the client (`dsn`);
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            * Whether or not the Postfix `master` process is running (`postfix_up`);
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            * Drinks from the syslog stream directly.
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            # Deployment
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            The default way of rolling out this exporter is as a Docker container.  For
         
     | 
| 
      
 29 
     | 
    
         
            +
            that, you'll want to setup a volume for the postfix queue (making it
         
     | 
| 
      
 30 
     | 
    
         
            +
            available in the container as `/var/spool/postfix`), and some env
         
     | 
| 
      
 31 
     | 
    
         
            +
            vars.  You can also run it directly (via the gem), with the same env vars,
         
     | 
| 
      
 32 
     | 
    
         
            +
            and with the expectation that `/var/spool/postfix` is in the usual place.
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            If you configure the postfix-exporter to run in the same PID namespace as
         
     | 
| 
      
 35 
     | 
    
         
            +
            whatever it is that's running Postfix itself (either `--pid=host` or in the
         
     | 
| 
      
 36 
     | 
    
         
            +
            same NS namespace, a la k8s pods), then the `postfix_up` metric will be
         
     | 
| 
      
 37 
     | 
    
         
            +
            valid, otherwise it'll be random (but unlikely to be correct).
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            ## Environment Variables
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            * `SYSLOG_SOCKET` -- where to read raw syslog events from.  If you're
         
     | 
| 
      
 43 
     | 
    
         
            +
              running Postfix in an isolated container, you can probably hook up
         
     | 
| 
      
 44 
     | 
    
         
            +
              `/dev/log` directly between containers with a bit of shenanigans,
         
     | 
| 
      
 45 
     | 
    
         
            +
              otherwise configure your syslog daemon to write out a copy of all `mail`
         
     | 
| 
      
 46 
     | 
    
         
            +
              facility messages to a custom socket.  Alternately, if you use logstash,
         
     | 
| 
      
 47 
     | 
    
         
            +
              take a look at the [`syslogstash`
         
     | 
| 
      
 48 
     | 
    
         
            +
              gem](https://rubygems.org/gems/syslogstash) and
         
     | 
| 
      
 49 
     | 
    
         
            +
              [container](https://hub.docker.com/r/discourse/syslogstash/), and go forth
         
     | 
| 
      
 50 
     | 
    
         
            +
              and sin no more.
         
     | 
| 
         @@ -0,0 +1,183 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'rack'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'prometheus/middleware/exporter'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'socket'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'rack/handler/webrick'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'logger'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            prometheus = Prometheus::Client.registry
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            prometheus.gauge(:postfix_exporter_start_time_seconds, "When this process started up").set({}, Time.now.to_f)
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            oldest = prometheus.gauge(:postfix_oldest_message_timestamp_seconds, "Queue time of the oldest message")
         
     | 
| 
      
 14 
     | 
    
         
            +
            mailq  = prometheus.gauge(:postfix_queue_size, "Number of messages in the mail queue")
         
     | 
| 
      
 15 
     | 
    
         
            +
            q_err  = prometheus.counter(:postfix_queue_processing_error_total, "Exceptions raised whilst scanning the Postfix queue")
         
     | 
| 
      
 16 
     | 
    
         
            +
            up     = prometheus.gauge(:postfix_up, "Whether the master process is running or not")
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            Thread.abort_on_exception = true
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            Thread.new do
         
     | 
| 
      
 21 
     | 
    
         
            +
            	loop do
         
     | 
| 
      
 22 
     | 
    
         
            +
            		begin
         
     | 
| 
      
 23 
     | 
    
         
            +
            			%w{incoming active corrupt hold}.each do |q|
         
     | 
| 
      
 24 
     | 
    
         
            +
            				mailq.set({ queue: q }, Dir["/var/spool/postfix/#{q}/*"].size)
         
     | 
| 
      
 25 
     | 
    
         
            +
            			end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            			# deferred is special, because it's often hueg it gets sharded into
         
     | 
| 
      
 28 
     | 
    
         
            +
            			# multiple subdirectories
         
     | 
| 
      
 29 
     | 
    
         
            +
            			mailq.set({ queue: 'deferred' }, Dir["/var/spool/postfix/deferred/*/*"].size)
         
     | 
| 
      
 30 
     | 
    
         
            +
            		rescue StandardError => ex
         
     | 
| 
      
 31 
     | 
    
         
            +
            			$stderr.puts "Error while monitoring queue sizes: #{ex.message} (#{ex.class})"
         
     | 
| 
      
 32 
     | 
    
         
            +
            			$stderr.puts ex.backtrace.map { |l| "  #{l}" }.join("\n")
         
     | 
| 
      
 33 
     | 
    
         
            +
            			q_err.increment(class: ex.class.to_s, phase: "scan")
         
     | 
| 
      
 34 
     | 
    
         
            +
            		end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            		begin
         
     | 
| 
      
 37 
     | 
    
         
            +
            			master_pid = File.read("/var/spool/postfix/pid/master.pid").to_i
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            			if master_pid > 1
         
     | 
| 
      
 40 
     | 
    
         
            +
            				Process.kill(0, master_pid)
         
     | 
| 
      
 41 
     | 
    
         
            +
            				# If we get here, then the process exists, and
         
     | 
| 
      
 42 
     | 
    
         
            +
            				# that'll do for our purposes
         
     | 
| 
      
 43 
     | 
    
         
            +
            				up.set({}, 1)
         
     | 
| 
      
 44 
     | 
    
         
            +
            			else
         
     | 
| 
      
 45 
     | 
    
         
            +
            				up.set({}, 0)
         
     | 
| 
      
 46 
     | 
    
         
            +
            			end
         
     | 
| 
      
 47 
     | 
    
         
            +
            		rescue Errno::ENOENT, Errno::ESRCH, Errno::EACCES
         
     | 
| 
      
 48 
     | 
    
         
            +
            			up.set({}, 0)
         
     | 
| 
      
 49 
     | 
    
         
            +
            		rescue Errno::EPERM
         
     | 
| 
      
 50 
     | 
    
         
            +
            			# Ironically, we don't need to be able to *actually*
         
     | 
| 
      
 51 
     | 
    
         
            +
            			# signal the process; EPERM means it exists and is running
         
     | 
| 
      
 52 
     | 
    
         
            +
            			# as someone more privileged than us, which is enough
         
     | 
| 
      
 53 
     | 
    
         
            +
            			# for our purposes
         
     | 
| 
      
 54 
     | 
    
         
            +
            			up.set({}, 1)
         
     | 
| 
      
 55 
     | 
    
         
            +
            		rescue StandardError => ex
         
     | 
| 
      
 56 
     | 
    
         
            +
            			$stderr.puts "Error while checking master process: #{ex.message} (#{ex.class})"
         
     | 
| 
      
 57 
     | 
    
         
            +
            			$stderr.puts ex.backtrace.map { |l| "  #{l}" }.join("\n")
         
     | 
| 
      
 58 
     | 
    
         
            +
            			q_err.increment(class: ex.class.to_s, phase: "up")
         
     | 
| 
      
 59 
     | 
    
         
            +
            		end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            		sleep 5
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
            	end
         
     | 
| 
      
 64 
     | 
    
         
            +
            end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
            Thread.new do
         
     | 
| 
      
 67 
     | 
    
         
            +
            	earliest_ctime = ->(glob) do
         
     | 
| 
      
 68 
     | 
    
         
            +
            		# There is seemingly no way to unset or remove a gauge metric in the Ruby
         
     | 
| 
      
 69 
     | 
    
         
            +
            		# implementation of the prom exporter.  As a hack, we return the current
         
     | 
| 
      
 70 
     | 
    
         
            +
            		# time in cases where there is nothing to sample.
         
     | 
| 
      
 71 
     | 
    
         
            +
            		now = Time.now.to_i
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
            		Dir[glob].lazy.map do |n|
         
     | 
| 
      
 74 
     | 
    
         
            +
            			begin
         
     | 
| 
      
 75 
     | 
    
         
            +
            				File.stat(n).ctime.to_i
         
     | 
| 
      
 76 
     | 
    
         
            +
            			rescue Errno::ENOENT
         
     | 
| 
      
 77 
     | 
    
         
            +
            				now
         
     | 
| 
      
 78 
     | 
    
         
            +
            			end
         
     | 
| 
      
 79 
     | 
    
         
            +
            		end.min || now
         
     | 
| 
      
 80 
     | 
    
         
            +
            	end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
            	loop do
         
     | 
| 
      
 83 
     | 
    
         
            +
            		begin
         
     | 
| 
      
 84 
     | 
    
         
            +
            			%w{incoming active corrupt hold}.each do |q|
         
     | 
| 
      
 85 
     | 
    
         
            +
            				oldest.set({ queue: q }, earliest_ctime["/var/spool/postfix/#{q}/*"])
         
     | 
| 
      
 86 
     | 
    
         
            +
            			end
         
     | 
| 
      
 87 
     | 
    
         
            +
            			oldest.set({ queue: 'deferred' }, earliest_ctime["/var/spool/postfix/deferred/*/*"])
         
     | 
| 
      
 88 
     | 
    
         
            +
            		rescue StandardError => ex
         
     | 
| 
      
 89 
     | 
    
         
            +
            			$stderr.puts "Error while sampling message ages: #{ex.message} (#{ex.class})"
         
     | 
| 
      
 90 
     | 
    
         
            +
            			$stderr.puts ex.backtrace.map { |l| "  #{l}" }.join("\n")
         
     | 
| 
      
 91 
     | 
    
         
            +
            			q_err.increment(class: ex.class.to_s, phase: "stat")
         
     | 
| 
      
 92 
     | 
    
         
            +
            		end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
            		# stat()ing all the files in a large queue could potentially be quite
         
     | 
| 
      
 95 
     | 
    
         
            +
            		# expensive, so we sample this data less frequently.
         
     | 
| 
      
 96 
     | 
    
         
            +
            		sleep 60
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
            	end
         
     | 
| 
      
 99 
     | 
    
         
            +
            end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
            if ENV["SYSLOG_SOCKET"]
         
     | 
| 
      
 102 
     | 
    
         
            +
            	delays     = prometheus.summary(:postfix_delivery_delays, "Distribution of time taken to deliver (or bounce) messages")
         
     | 
| 
      
 103 
     | 
    
         
            +
            	connects   = prometheus.counter(:postfix_smtpd_connections_total, "Connections to smtpd")
         
     | 
| 
      
 104 
     | 
    
         
            +
            	active     = prometheus.gauge(:postfix_smtpd_active_connections, "Current connections to smtpd")
         
     | 
| 
      
 105 
     | 
    
         
            +
            	incoming   = prometheus.counter(:postfix_incoming_delivery_attempts_total, "Delivery attempts, labelled by dsn and status")
         
     | 
| 
      
 106 
     | 
    
         
            +
            	messages   = prometheus.counter(:postfix_log_messages_total, "Syslog messages received, labelled by how it was handled")
         
     | 
| 
      
 107 
     | 
    
         
            +
            	log_errors = prometheus.counter(:postfix_log_processing_error_total, "Exceptions raised whilst processing log messages")
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
            	Thread.new do
         
     | 
| 
      
 110 
     | 
    
         
            +
            		begin
         
     | 
| 
      
 111 
     | 
    
         
            +
            			s = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0)
         
     | 
| 
      
 112 
     | 
    
         
            +
            			s.bind(Socket.pack_sockaddr_un(ENV["SYSLOG_SOCKET"]))
         
     | 
| 
      
 113 
     | 
    
         
            +
            		rescue Errno::EEXIST, Errno::EADDRINUSE
         
     | 
| 
      
 114 
     | 
    
         
            +
            			File.unlink ENV["SYSLOG_SOCKET"]
         
     | 
| 
      
 115 
     | 
    
         
            +
            			retry
         
     | 
| 
      
 116 
     | 
    
         
            +
            		end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
            		loop do
         
     | 
| 
      
 119 
     | 
    
         
            +
            			begin
         
     | 
| 
      
 120 
     | 
    
         
            +
            				msg = s.recvmsg.first
         
     | 
| 
      
 121 
     | 
    
         
            +
            				if msg =~ %r{postfix/.* delay=(\d+(\.\d+)?), .* dsn=(\d+\.\d+\.\d+), status=(\w+)}
         
     | 
| 
      
 122 
     | 
    
         
            +
            					delay  = $1.to_f
         
     | 
| 
      
 123 
     | 
    
         
            +
            					dsn    = $3
         
     | 
| 
      
 124 
     | 
    
         
            +
            					status = $4
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
            					if status == "bounced" or status == "sent"
         
     | 
| 
      
 127 
     | 
    
         
            +
            						delays.observe({dsn: dsn, status: status}, delay)
         
     | 
| 
      
 128 
     | 
    
         
            +
            					end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
            					messages.increment(type: "delay")
         
     | 
| 
      
 131 
     | 
    
         
            +
            				elsif msg =~ %r{postfix/smtpd\[\d+\]: connect from }
         
     | 
| 
      
 132 
     | 
    
         
            +
            					connects.increment({})
         
     | 
| 
      
 133 
     | 
    
         
            +
            					active.send(:synchronize) { active.set({}, active.get({}) || 0 + 1) }
         
     | 
| 
      
 134 
     | 
    
         
            +
            					messages.increment(type: "connect")
         
     | 
| 
      
 135 
     | 
    
         
            +
            				elsif msg =~ %r{postfix/smtpd\[\d+\]: disconnect from }
         
     | 
| 
      
 136 
     | 
    
         
            +
            					active.send(:synchronize) do
         
     | 
| 
      
 137 
     | 
    
         
            +
            					  new = (active.get({}) || 0) - 1
         
     | 
| 
      
 138 
     | 
    
         
            +
            					  # If we start running mid-stream,
         
     | 
| 
      
 139 
     | 
    
         
            +
            					  # we might end up seeing more
         
     | 
| 
      
 140 
     | 
    
         
            +
            					  # disconnects than connections,
         
     | 
| 
      
 141 
     | 
    
         
            +
            					  # which would be confusing
         
     | 
| 
      
 142 
     | 
    
         
            +
            					  new = 0 if new < 0
         
     | 
| 
      
 143 
     | 
    
         
            +
            					  active.set({}, new)
         
     | 
| 
      
 144 
     | 
    
         
            +
            					end
         
     | 
| 
      
 145 
     | 
    
         
            +
            					messages.increment(type: "disconnect")
         
     | 
| 
      
 146 
     | 
    
         
            +
            				elsif msg =~ %r{postfix/smtpd\[\d+\]: [A-F0-9]+: client=}
         
     | 
| 
      
 147 
     | 
    
         
            +
            					incoming.increment(dsn: "2.0.0", status: "queued")
         
     | 
| 
      
 148 
     | 
    
         
            +
            					messages.increment(type: "queued")
         
     | 
| 
      
 149 
     | 
    
         
            +
            				elsif msg =~ %r{postfix/smtpd\[\d+\]: NOQUEUE: reject: RCPT from \S+: \d{3} (\d+\.\d+\.\d+) }
         
     | 
| 
      
 150 
     | 
    
         
            +
            					incoming.increment(dsn: $1, status: "rejected")
         
     | 
| 
      
 151 
     | 
    
         
            +
            					messages.increment(type: "noqueue")
         
     | 
| 
      
 152 
     | 
    
         
            +
            				else
         
     | 
| 
      
 153 
     | 
    
         
            +
            					messages.increment(type: "ignored")
         
     | 
| 
      
 154 
     | 
    
         
            +
            				end
         
     | 
| 
      
 155 
     | 
    
         
            +
            			rescue StandardError => ex
         
     | 
| 
      
 156 
     | 
    
         
            +
            				$stderr.puts "Error while receiving postfix logs: #{ex.message} (#{ex.class})"
         
     | 
| 
      
 157 
     | 
    
         
            +
            				$stderr.puts ex.backtrace.map { |l| "  #{l}" }.join("\n")
         
     | 
| 
      
 158 
     | 
    
         
            +
            				log_errors.increment(class: ex.class.to_s)
         
     | 
| 
      
 159 
     | 
    
         
            +
            				sleep 1
         
     | 
| 
      
 160 
     | 
    
         
            +
            			end
         
     | 
| 
      
 161 
     | 
    
         
            +
            		end
         
     | 
| 
      
 162 
     | 
    
         
            +
            	end
         
     | 
| 
      
 163 
     | 
    
         
            +
            end
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
            app = Rack::Builder.new
         
     | 
| 
      
 166 
     | 
    
         
            +
            app.use Rack::Deflater, if: ->(_, _, _, body) { body.any? && body[0].length > 512 }
         
     | 
| 
      
 167 
     | 
    
         
            +
            app.use Prometheus::Middleware::Exporter
         
     | 
| 
      
 168 
     | 
    
         
            +
            app.run ->(env) { [404, {'Content-Type' => 'text/plain'}, ['NOPE NOPE NOPE NOPE']] }
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
            logger = Logger.new($stderr)
         
     | 
| 
      
 171 
     | 
    
         
            +
            logger.level = Logger::INFO
         
     | 
| 
      
 172 
     | 
    
         
            +
            logger.formatter = proc { |s, t, p, m| "WEBrick: #{m}\n" }
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
            # This is the only way to get the Rack-mediated webrick to listen on both
         
     | 
| 
      
 175 
     | 
    
         
            +
            # INADDR_ANY and IN6ADDR_ANY on libcs that don't support getaddrinfo("*")
         
     | 
| 
      
 176 
     | 
    
         
            +
            # (ie musl-libc).  Setting `Host: '*'` barfs on the above-mentioned buggy(?)
         
     | 
| 
      
 177 
     | 
    
         
            +
            # libcs, `Host: '::'` fails on newer rubies (because they use
         
     | 
| 
      
 178 
     | 
    
         
            +
            # setsockopt(V6ONLY) by default), and with RACK_ENV at its default of
         
     | 
| 
      
 179 
     | 
    
         
            +
            # "development", it only listens on localhost.  And even *this* only works
         
     | 
| 
      
 180 
     | 
    
         
            +
            # on Rack 2, because before that the non-development default listen address
         
     | 
| 
      
 181 
     | 
    
         
            +
            # was "0.0.0.0"!
         
     | 
| 
      
 182 
     | 
    
         
            +
            ENV['RACK_ENV'] = "none"
         
     | 
| 
      
 183 
     | 
    
         
            +
            Rack::Handler::WEBrick.run app, Port: 9154, Logger: logger, AccessLog: []
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            begin
         
     | 
| 
      
 2 
     | 
    
         
            +
            	require 'git-version-bump'
         
     | 
| 
      
 3 
     | 
    
         
            +
            rescue LoadError
         
     | 
| 
      
 4 
     | 
    
         
            +
            	nil
         
     | 
| 
      
 5 
     | 
    
         
            +
            end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            Gem::Specification.new do |s|
         
     | 
| 
      
 8 
     | 
    
         
            +
            	s.name = "postfix-exporter"
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            	s.version = GVB.version rescue "0.0.0.1.NOGVB"
         
     | 
| 
      
 11 
     | 
    
         
            +
            	s.date    = GVB.date    rescue Time.now.strftime("%Y-%m-%d")
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            	s.platform = Gem::Platform::RUBY
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            	s.summary  = "Export Prometheus statistics for a Postfix server"
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            	s.authors  = ["Matt Palmer"]
         
     | 
| 
      
 18 
     | 
    
         
            +
            	s.email    = ["matt.palmer@discourse.org"]
         
     | 
| 
      
 19 
     | 
    
         
            +
            	s.homepage = "https://github.com/discourse/postfix-exporter"
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            	s.files = `git ls-files -z`.split("\0").reject { |f| f =~ /^(G|spec|Dockerfile|Rakefile)/ }
         
     | 
| 
      
 22 
     | 
    
         
            +
            	s.executables = ["postfix-exporter"]
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            	s.required_ruby_version = ">= 2.1.0"
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            	s.add_runtime_dependency 'prometheus-client', '~> 0.7', '< 0.10'
         
     | 
| 
      
 27 
     | 
    
         
            +
            	s.add_runtime_dependency 'rack', '~> 2.0'
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            	s.add_development_dependency 'bundler'
         
     | 
| 
      
 30 
     | 
    
         
            +
            	s.add_development_dependency 'github-release'
         
     | 
| 
      
 31 
     | 
    
         
            +
            	s.add_development_dependency 'rake'
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,123 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: postfix-exporter
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.0
         
     | 
| 
      
 5 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 6 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Matt Palmer
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire:
         
     | 
| 
      
 9 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 10 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2020-10-07 00:00:00.000000000 Z
         
     | 
| 
      
 12 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 13 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: prometheus-client
         
     | 
| 
      
 15 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 17 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 18 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 19 
     | 
    
         
            +
                    version: '0.7'
         
     | 
| 
      
 20 
     | 
    
         
            +
                - - "<"
         
     | 
| 
      
 21 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 22 
     | 
    
         
            +
                    version: '0.10'
         
     | 
| 
      
 23 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 24 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 25 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 26 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 27 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 28 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 29 
     | 
    
         
            +
                    version: '0.7'
         
     | 
| 
      
 30 
     | 
    
         
            +
                - - "<"
         
     | 
| 
      
 31 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 32 
     | 
    
         
            +
                    version: '0.10'
         
     | 
| 
      
 33 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 34 
     | 
    
         
            +
              name: rack
         
     | 
| 
      
 35 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 36 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 37 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 38 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 39 
     | 
    
         
            +
                    version: '2.0'
         
     | 
| 
      
 40 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 41 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 42 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 43 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 44 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 45 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 46 
     | 
    
         
            +
                    version: '2.0'
         
     | 
| 
      
 47 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 48 
     | 
    
         
            +
              name: bundler
         
     | 
| 
      
 49 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 50 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 51 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 52 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 53 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 54 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 55 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 56 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 57 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 58 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 59 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 60 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 61 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 62 
     | 
    
         
            +
              name: github-release
         
     | 
| 
      
 63 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 64 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 65 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 66 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 67 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 68 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 69 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 70 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 71 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 72 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 73 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 74 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 75 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 76 
     | 
    
         
            +
              name: rake
         
     | 
| 
      
 77 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 78 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 79 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 80 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 81 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 82 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 83 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 84 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 85 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 86 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 87 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 88 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 89 
     | 
    
         
            +
            description:
         
     | 
| 
      
 90 
     | 
    
         
            +
            email:
         
     | 
| 
      
 91 
     | 
    
         
            +
            - matt.palmer@discourse.org
         
     | 
| 
      
 92 
     | 
    
         
            +
            executables:
         
     | 
| 
      
 93 
     | 
    
         
            +
            - postfix-exporter
         
     | 
| 
      
 94 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 95 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 96 
     | 
    
         
            +
            files:
         
     | 
| 
      
 97 
     | 
    
         
            +
            - ".gitignore"
         
     | 
| 
      
 98 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 99 
     | 
    
         
            +
            - bin/postfix-exporter
         
     | 
| 
      
 100 
     | 
    
         
            +
            - postfix-exporter.gemspec
         
     | 
| 
      
 101 
     | 
    
         
            +
            homepage: https://github.com/discourse/postfix-exporter
         
     | 
| 
      
 102 
     | 
    
         
            +
            licenses: []
         
     | 
| 
      
 103 
     | 
    
         
            +
            metadata: {}
         
     | 
| 
      
 104 
     | 
    
         
            +
            post_install_message:
         
     | 
| 
      
 105 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 106 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 107 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 108 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 109 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 110 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 111 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 112 
     | 
    
         
            +
                  version: 2.1.0
         
     | 
| 
      
 113 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 114 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 115 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 116 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 117 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 118 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 119 
     | 
    
         
            +
            rubygems_version: 3.0.3
         
     | 
| 
      
 120 
     | 
    
         
            +
            signing_key:
         
     | 
| 
      
 121 
     | 
    
         
            +
            specification_version: 4
         
     | 
| 
      
 122 
     | 
    
         
            +
            summary: Export Prometheus statistics for a Postfix server
         
     | 
| 
      
 123 
     | 
    
         
            +
            test_files: []
         
     |