-
Notifications
You must be signed in to change notification settings - Fork 5.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
phpfpm plugin doesn't return any data #502
Comments
@sofixa your test output isn't in fact working, it should be showing measurements @kureikain any ideas why he wouldn't be getting any metrics? |
@sparrc My apologize for not answering sooner. @sofixa Hi, Can you post your example config for My guess is this a permission issue. Telegraf may run under telegraf user. And it doesn't have permission to access socket. From your output, I can see that you are running under root to get stats. Here is some information for further output(to verify user):
also, you can try to run this command under
If you use To verify, you may try to run When using
|
Hi, thanks for your response.
My config is simple:
Note:without the localhost: bit i was getting an error, which wasn't due to a rights issue, since i have been running my sock as 666. And sadly i can't use the http method, since my site has lots of redirections, and i'd have to spend some considerable time trying to find which one i'd need to add an exception to so that my status page stays visible(it's a Wordpress site with i18n and CDN caused redirections). |
I have noticed the same with fcgi/cgi. Works through nginx:
I have tried with all of these, one at a time or course: only the http url returns metrics. I get no errors, just the line |
@lassizci fcgi ideally should works. I don't write that part of code. Will look into it. Thanks for your Nginx configuration to limit access. @sofixa As @lassizci pointed out, you can use
Will push a fix for this. I still not sure why it won't work even when you run under root. Will you able to compile this file and run it, it will output some info, I probably should return those error report into the plugin itself. File Content: package main
// FastCGI client to request via socket
// Copyright 2012 Junqing Tan <ivan@mysqlab.net> and The Go Authors
// Use of this source code is governed by a BSD-style
// Part of source code is from Go fcgi package
// Fix bug: Can't recive more than 1 record untill FCGI_END_REQUEST 2012-09-15
// By: wofeiwo
import (
"bufio"
"bytes"
"encoding/binary"
"errors"
"io"
"log"
"net"
"strconv"
"sync"
)
const FCGI_LISTENSOCK_FILENO uint8 = 0
const FCGI_HEADER_LEN uint8 = 8
const VERSION_1 uint8 = 1
const FCGI_NULL_REQUEST_ID uint8 = 0
const FCGI_KEEP_CONN uint8 = 1
const (
FCGI_BEGIN_REQUEST uint8 = iota + 1
FCGI_ABORT_REQUEST
FCGI_END_REQUEST
FCGI_PARAMS
FCGI_STDIN
FCGI_STDOUT
FCGI_STDERR
FCGI_DATA
FCGI_GET_VALUES
FCGI_GET_VALUES_RESULT
FCGI_UNKNOWN_TYPE
FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
)
const (
FCGI_RESPONDER uint8 = iota + 1
FCGI_AUTHORIZER
FCGI_FILTER
)
const (
FCGI_REQUEST_COMPLETE uint8 = iota
FCGI_CANT_MPX_CONN
FCGI_OVERLOADED
FCGI_UNKNOWN_ROLE
)
const (
FCGI_MAX_CONNS string = "MAX_CONNS"
FCGI_MAX_REQS string = "MAX_REQS"
FCGI_MPXS_CONNS string = "MPXS_CONNS"
)
const (
maxWrite = 6553500 // maximum record body
maxPad = 255
)
type header struct {
Version uint8
Type uint8
Id uint16
ContentLength uint16
PaddingLength uint8
Reserved uint8
}
// for padding so we don't have to allocate all the time
// not synchronized because we don't care what the contents are
var pad [maxPad]byte
func (h *header) init(recType uint8, reqId uint16, contentLength int) {
h.Version = 1
h.Type = recType
h.Id = reqId
h.ContentLength = uint16(contentLength)
h.PaddingLength = uint8(-contentLength & 7)
}
type record struct {
h header
buf [maxWrite + maxPad]byte
}
func (rec *record) read(r io.Reader) (err error) {
if err = binary.Read(r, binary.BigEndian, &rec.h); err != nil {
return err
}
if rec.h.Version != 1 {
return errors.New("fcgi: invalid header version")
}
n := int(rec.h.ContentLength) + int(rec.h.PaddingLength)
if _, err = io.ReadFull(r, rec.buf[:n]); err != nil {
return err
}
return nil
}
func (r *record) content() []byte {
return r.buf[:r.h.ContentLength]
}
type FCGIClient struct {
mutex sync.Mutex
rwc io.ReadWriteCloser
h header
buf bytes.Buffer
keepAlive bool
}
func NewClient(h string, args ...interface{}) (fcgi *FCGIClient, err error) {
var conn net.Conn
if len(args) != 1 {
err = errors.New("fcgi: not enough params")
return
}
switch args[0].(type) {
case int:
addr := h + ":" + strconv.FormatInt(int64(args[0].(int)), 10)
conn, err = net.Dial("tcp", addr)
case string:
laddr := net.UnixAddr{Name: args[0].(string), Net: h}
conn, err = net.DialUnix(h, nil, &laddr)
default:
err = errors.New("fcgi: we only accept int (port) or string (socket) params.")
}
fcgi = &FCGIClient{
rwc: conn,
keepAlive: false,
}
return
}
func (client *FCGIClient) writeRecord(recType uint8, reqId uint16, content []byte) (err error) {
client.mutex.Lock()
defer client.mutex.Unlock()
client.buf.Reset()
client.h.init(recType, reqId, len(content))
if err := binary.Write(&client.buf, binary.BigEndian, client.h); err != nil {
return err
}
if _, err := client.buf.Write(content); err != nil {
return err
}
if _, err := client.buf.Write(pad[:client.h.PaddingLength]); err != nil {
return err
}
_, err = client.rwc.Write(client.buf.Bytes())
return err
}
func (client *FCGIClient) writeBeginRequest(reqId uint16, role uint16, flags uint8) error {
b := [8]byte{byte(role >> 8), byte(role), flags}
return client.writeRecord(FCGI_BEGIN_REQUEST, reqId, b[:])
}
func (client *FCGIClient) writeEndRequest(reqId uint16, appStatus int, protocolStatus uint8) error {
b := make([]byte, 8)
binary.BigEndian.PutUint32(b, uint32(appStatus))
b[4] = protocolStatus
return client.writeRecord(FCGI_END_REQUEST, reqId, b)
}
func (client *FCGIClient) writePairs(recType uint8, reqId uint16, pairs map[string]string) error {
w := newWriter(client, recType, reqId)
b := make([]byte, 8)
for k, v := range pairs {
n := encodeSize(b, uint32(len(k)))
n += encodeSize(b[n:], uint32(len(v)))
if _, err := w.Write(b[:n]); err != nil {
return err
}
if _, err := w.WriteString(k); err != nil {
return err
}
if _, err := w.WriteString(v); err != nil {
return err
}
}
w.Close()
return nil
}
func readSize(s []byte) (uint32, int) {
if len(s) == 0 {
return 0, 0
}
size, n := uint32(s[0]), 1
if size&(1<<7) != 0 {
if len(s) < 4 {
return 0, 0
}
n = 4
size = binary.BigEndian.Uint32(s)
size &^= 1 << 31
}
return size, n
}
func readString(s []byte, size uint32) string {
if size > uint32(len(s)) {
return ""
}
return string(s[:size])
}
func encodeSize(b []byte, size uint32) int {
if size > 127 {
size |= 1 << 31
binary.BigEndian.PutUint32(b, size)
return 4
}
b[0] = byte(size)
return 1
}
// bufWriter encapsulates bufio.Writer but also closes the underlying stream when
// Closed.
type bufWriter struct {
closer io.Closer
*bufio.Writer
}
func (w *bufWriter) Close() error {
if err := w.Writer.Flush(); err != nil {
w.closer.Close()
return err
}
return w.closer.Close()
}
func newWriter(c *FCGIClient, recType uint8, reqId uint16) *bufWriter {
s := &streamWriter{c: c, recType: recType, reqId: reqId}
w := bufio.NewWriterSize(s, maxWrite)
return &bufWriter{s, w}
}
// streamWriter abstracts out the separation of a stream into discrete records.
// It only writes maxWrite bytes at a time.
type streamWriter struct {
c *FCGIClient
recType uint8
reqId uint16
}
func (w *streamWriter) Write(p []byte) (int, error) {
nn := 0
for len(p) > 0 {
n := len(p)
if n > maxWrite {
n = maxWrite
}
if err := w.c.writeRecord(w.recType, w.reqId, p[:n]); err != nil {
return nn, err
}
nn += n
p = p[n:]
}
return nn, nil
}
func (w *streamWriter) Close() error {
// send empty record to close the stream
return w.c.writeRecord(w.recType, w.reqId, nil)
}
func (client *FCGIClient) Request(env map[string]string, reqStr string) (retout []byte, reterr []byte, err error) {
var reqId uint16 = 1
defer client.rwc.Close()
err = client.writeBeginRequest(reqId, uint16(FCGI_RESPONDER), 0)
if err != nil {
return
}
err = client.writePairs(FCGI_PARAMS, reqId, env)
if err != nil {
return
}
if len(reqStr) > 0 {
err = client.writeRecord(FCGI_STDIN, reqId, []byte(reqStr))
if err != nil {
return
}
}
rec := &record{}
var err1 error
// recive untill EOF or FCGI_END_REQUEST
for {
err1 = rec.read(client.rwc)
if err1 != nil {
if err1 != io.EOF {
err = err1
}
break
}
switch {
case rec.h.Type == FCGI_STDOUT:
retout = append(retout, rec.content()...)
case rec.h.Type == FCGI_STDERR:
reterr = append(reterr, rec.content()...)
case rec.h.Type == FCGI_END_REQUEST:
fallthrough
default:
break
}
}
return
}
func main() {
fcgi, _ := NewClient("unix", "/var/run/php5-fpm.sock")
resOut, resErr, err := fcgi.Request(map[string]string{
"SCRIPT_NAME": "/status",
"SCRIPT_FILENAME": "status",
"REQUEST_METHOD": "GET",
}, "")
log.Printf("Output: %v\n", string(resOut))
log.Printf("Sock Err: %v\n", string(resErr))
log.Printf("Err: %v\n", err)
} And run it:
Maybe we can find something about it. I will submit a PRs to make the plugin return error information so Telegraf can log the in its log. |
I get the following when running your script
As for the http method, i am not worried about unauthorized access, that's easy; my problem is that my PHP code immediately redirects the request(since i run a WordPress blog with internalization, which intercepts all requests and if they are not prefixed with a language name, sends them to the default one, and then demands the info) and returns a 404 every time, and even if i somehow manage to insert an exception for it to ignore /php-fpm-status, i'd have to do it each time i upgrade the plugins.. So not very practical... |
When i put the actual path to my PHP-FPM status page i get it properly with your script:
Which is odd, because i can't even wget into it:
|
@sofixa You cannot wget it because it use fastcgi protocol, not http protocol. So now I know what is the problem. Problem is that you don't use So I'm going to create a PR (#538) to allow you supply the path to socket like this:
Are you ok with this solution? |
Yeap, sounds perfect. |
And i probably should have mentioned i am not using /status, i am using it for nginx's status page, sorry... |
@sofixa yeah, this is because the plugin doesn't report error properly, it just fail silently. I fixed all kind of this issue in #538 . Also, you can use only:
You don't have to specify the host field. It may take awhile for #538 to be merge and release. So you may want to compile it yourself. It's rebase on current master. |
Okay, thanks a lot, mate. |
I have tried using the phpfpm plugin, and altough it runs successfully, it doesn't return any data.
Here's the output from the command:
Without -test i have:
So, in theory, it should work, but in InfluxDB i have absolutely nothing.
PHP-FPM is working, and i have used it, and when running a script that shows the status of PHP-FPM i get plenty of data:
What could be the problem? What metrics should the phpfpm plugin collect?
The text was updated successfully, but these errors were encountered: