Git Product home page Git Product logo

gelf-php's Introduction

gelf-php Latest Stable Version Total Downloads

Build Status Code Coverage Scrutinizer Quality Score

A php implementation to send log-files to a gelf compatible backend like Graylog2. This library conforms to the PSR standards in regards to structure (4), coding-style (1, 2) and logging (3).

It's a loosely based on the original Graylog2 gelf-php and mlehner's fork.

Stable release and deprecation of the original graylog2/gelf-php

This implementation became the official PHP GELF library on 2013-12-19 and is now released as graylog2/gelf-php. The old library became deprecated at the same time and it's recommended to upgrade.

Since the deprecated library never got a stable release, we decided keep it available as v0.1. This means: If you have a project based on the deprecated library but no time to upgrade to version 1.0, we recommend to change your composer.json as following:

    "require": {
       // ...
       "graylog2/gelf-php": "0.1.*"
       // ...
    }

After running an additional composer update everything should work as expected.

A note on PHP versions before 5.6

I tried to keep backwards compatibility alive as long as possible, but it 2021 it's not feasible anymore to deal with the pain of dependency management for PHP 5.3.3 - 5.5.latest. They are EOL for many years anyway.

If you are somehow stuck on <5.6, you can use gelf-php up to version 1.6.5.

I decided against a semver-compliant increase from 1.x to 2.x on purpose.

Usage

Recommended installation via composer:

Add gelf-php to composer.json either by running composer require graylog2/gelf-php or by defining it manually:

"require": {
   // ...
   "graylog2/gelf-php": "~1.5"
   // ...
}

Reinstall dependencies: composer install

Examples

For usage examples, go to /examples.

Muting connection and transport errors

Oftentimes projects run into the situation where they don't want to raise exceptions for logging-errors. Since the standard transports like Udp, Tcp and Http can be kind of noise for fwrite/fopen errors, gelf-php provides a IgnoreErrorTransportWrapper. This class can decorate any AbstractTransport and will mute all exceptions.

How this applies in practice can be seen in the advanced-example.

If you use gelf-php in conjunction with monolog/symfony, the following snippet should help you with properly setting up your logging backend.

Assuming you have a typical monolog config:

monolog:
  handlers:
    graylog:
      type: service
      id: monolog.gelf_handler
      level: debug

You only need to properly define the symfony-service gelf-handler:

services:
  monolog.gelf_handler:
    class: Monolog\Handler\GelfHandler
    arguments:
        - @gelf.publisher
        - 'warning' #monolog config is ignored with custom service level has to be redefined here (default : debug), you should probably use parameters eg: '%gelf_level%'
    
  gelf.publisher:
    class: Gelf\Publisher
    arguments: [@gelf.ignore_error_transport]
    
  gelf.ignore_error_transport:
    class: Gelf\Transport\IgnoreErrorTransportWrapper
    arguments: [@gelf.transport]
    
  gelf.transport:
    class: Gelf\Transport\UdpTransport # or Tcp, Amp, Http,...
    arguments: [] # ... whatever is required

HHVM

While HHVM is supported/tested, there are some restrictions to look out for:

  • Stream-context support is very limited (as of 2014) - especially regarding SSL - many use-cases might not work as expected (or not at all...)
  • fwrite does behave a little different

The failing unit-tests are skipped by default when running on HHVM. They are also all annotated with @group hhvm-failures. You can force to run those failures by setting FORCE_HHVM_TESTS=1 in the environment. Therefore you can specifically check the state of HHVM failures by running:

FORCE_HHVM_TESTS=1 hhvm vendor/bin/phpunit --group hhvm-failures

License

The library is licensed under the MIT license. For details check out the LICENSE file.

Development & Contributing

You are welcome to modify, extend and bugfix all you like. :-) If you have any questions/proposals/etc. you can contact me on Twitter (@bzikarsky) or message me on freenode#graylog2.

Tools

  1. composer, preferably a system-wide installation as composer
  2. PHPUnit
  3. Optional: PHP_CodeSniffer for PSR-X-compatibility checks

Steps

  1. Clone repository and cd into it: git clone [email protected]:bzikarsky/gelf-php && cd gelf-php
  2. Install dependencies: composer install
  3. Run unit-tests: vendor/bin/phpunit
  4. Check PSR compatibility: vendor/bin/phpcs --standard=PSR2 src tests examples

gelf-php's People

Contributors

betweenbrain avatar bzikarsky avatar carusogabriel avatar chris8934 avatar chrisnew avatar cosmologist avatar cseufert avatar dbu avatar evaldaskocys avatar evansims avatar gummibeer avatar h4cc avatar inventor96 avatar j4r3kb avatar jacobkiers avatar jeroendedauw avatar justindugas avatar lwillems avatar matthi4s avatar mrjgreen avatar ocramius avatar olegpro avatar pascalwild avatar seldaek avatar steffkes avatar tobion avatar toubsen avatar verfriemelt-dot-org avatar versh23 avatar xel1045 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gelf-php's Issues

Option to do connection via Proxy

Would be nice to have an option to initialize HttpTransport / socket-client via Proxy.
Maybe like this :

$opts = array('http' => array('proxy' => 'tcp://127.0.0.1:8080', 'request_fulluri' => true));
$context = stream_context_create($opts);

Replace all occurances of StringableClass

In some unittests a dummy class Gelf\Test\StringableClass is used.

  1. Replace all occurances with
$toString = $this->getMock('dummyClass', array('__toString'));
$toString->method('__toString')->willReturn('toString');
  1. Delete class
  2. Remove require_once for class in tests/bootstrap.php

Closed connections and timeouts on TcpTransport (was: Incomplete write: Only 0 of 358 written)

Hello! I have bug when i write log to GrayLog on the large load project. There is the bug:
Incomplete write: Only 0 of 358 written in /data/home/projects/payprocessing/classes/vendor/graylog2/gelf-php/src/Gelf/Transport/StreamSocketClient.php:212

Here is the message send to socket:

{
"version": "1.0",
"host": "pay-1.reserve.lan",
"short_message": "Redirect to https://www.platron.ru/payment_params.",
"full_message": "Redirect to https://www.platron.ru/payment_params.php?customer=5d44643437990b1774efb742ed1fb9a031005685\r\n(Process number: 84073)",
"level": 6,
"timestamp": 1484144247.0146,
"facility": "paypocessing",
"file": "Platron::payment"
}

I try to send id manual - and it's worked! So the problem in fwrite function. There is a notice in php documentation about fwrite function:

http://php.net/manual/en/function.fwrite.php

Some people say that when writing to a socket not all of the bytes requested to be written may be written. You may have to call fwrite again to write bytes that were not written the first time. (At least this is how the write() system call in UNIX works.)

I think you need to try fwrite_retry several times, before throw error, or, maybe, use some other function.

Too simple check in Gelf/Message leads to RuntimeException

It seems that i hit a rare condition in Gelf\Message::setAdditional:

if the contextPrefix in the GelfMessageFormatter is an empty string - no prefixing - and the context is an associative array (like from a doctrine select statement):

array:7 [
  "message" => "SELECT u0_.uuid AS uuid_0, u0_.username AS username_1, u0_.password AS password_2, u0_.roles AS roles_3, u0_.expired AS expired_4, u0_.locked AS locked_5, u0_.enabled AS enabled_6, u0_.credentials_expired AS credentials_expired_7 FROM "user" u0_ WHERE u0_.username = ?"
  "context" => array:1 [
    0 => "test"
  ]
  "level" => 100
  "level_name" => "DEBUG"
  "channel" => "doctrine"
  "datetime" => "1524690309.189944"
  "extra" => []
]

the condition if(!$key) will raise the RuntimeException (since !0 == true).

Validation fails when logging a zero value

Both of the following fail validation:

$logger->info(0);

and

$logger->info('0');

Why are these not valid log messages? Could the validator check for an empty string instead of a "falsey" value? Or are falsey values also forbidden by the graylog specification. Happy to try and submit a PR if required.

Hope you can help
Cheers

Why Logger::interpolate() is private ???

Hello,
I'm switching from an old GELF lib to yours and I needed to do some adjustments regarding our needs.

I was wondering if is there is any interest of having this following method to be private:

    private static function interpolate($message, array $context)
    {
        // build a replacement array with braces around the context keys
        $replace = array();
        foreach ($context as $key => $val) {
            $replace['{' . $key . '}'] = $val;
        }

        // interpolate replacement values into the message and return
        return strtr($message, $replace);
    }

Instead of simply overriding the interpolate like that:

    protected static function interpolate($message, array $context)
    {
        // Do some stufff
       return parent::interpolate($message, $context);
    }

I have to override the protected function initMessage($level, $message, array $context) method :/

Issues with setAdditional

Hello,

Is anyone having issues with $message->setAdditional($key,$value) function. Here is the test script

<?php
require_once __DIR__ . '/vendor/gelf-php/gelf-php-master/vendor/autoload.php';
$transport = new Gelf\Transport\TcpTransport("<IP>, <PORT>);
$publisher = new Gelf\Publisher();
$publisher->addTransport($transport);
$message = new Gelf\Message();
$message->setShortMessage("It works in TCP!")
        ->setLevel(\Psr\Log\LogLevel::ALERT)
        ->setFullMessage("There was a foo in bar")
        ->setFacility("local8")
;
$message->setAdditional("IP", "10.4.13.45");
//var_dump($message);
$publisher->publish($message);
var_dump($publisher);

it works without setAdditional. And I do not see an error with setAdditional part of the script but nothing gets pushed to the graylog server.

Message with boolean value is filtered out

Currently, if some key has boolean value, is wrongly converted empty string and filtered out as empty value. #38 solves this, but it's somehow stuck and also solves an another problem.

Publish/tag a version ?

Hi,

We have some issues with UDP transport and the 1.5.3 version (Incomplete write: Only 0 of XXX written)
d98632d seems to correct it. Can you publish a tagged version with these changes ?

Thx a lot !

Update PhpUnit version (dependency + tests)

Tests for this library rely on PhpUnit 4.x (via composer.json) at max 5.x (usage of getMock()). It'd make sense in the long run (e.g. 2.x) to update + refactor to a newer supported PhpUnit version (>= 7) as a dependency.

Make buildSocket exceptions catchable from the initialization of the StreamSocketClient

Hello everyone, and thank you for your great tool :)

Here is the use case we are dealing with right now:

We have our php-application which uses monolog for logging, and the Gelf\Publisher as publisher for GelfHandler.

It is initialized like this, and it works fine:

$logger->pushHandler(new GelfHandler(new Gelf\Publisher(new Gelf\HttpTransport('graylogserver',12201)),Logger::INFO));

The problem is that http communications are not reliable. The server might be down or someone can change the settings or whatever, causing an exception to be thrown, crashing the application because of logging. I noticed that the socket gets created the moment we try to send our first log, but that feels a little too late.

May I suggest some possible solutions:

  1. Create the socket the moment we initialize the HttpTransport, making the exception catchable the moment we initialize our handler. That way we won't have to try/catch on every $logger->log() call and still have a way to know of things going wrong
  2. Add a $HttpTransport->socketExists() that just tries to open the socket
  3. Simply set an option that disables the exception throwing for this specific case

If you agree I can make a pull request for this :)

Provide example configs for Symfony (was: How to configure IgnoreErrorTransportWrapper in config.yml symfony2 project ?)

I'm trying to stream logs from a symfony 2 app to a graylog 2 server using the gelf format.
My monolog configuration looks as follows:
monolog: handlers: graylog: type: gelf publisher: hostname: my-graylog-server.com port: 12201 level: debug formatter: app.gelf_formatter
I'm getting a connection refused error when the graylog server is not available.
[2017-07-28 16:03:25] app.ERROR: Failed to write to socket: fwrite(): send of 153 bytes failed with errno=111 Connection refused (8) [] []
This is why I'm looking for how to set IgnoreErrorTransportWrapper option in my config.yml
Any help would be greatly appreciated!

400 Bad Request when using SSL and SAN certificates

We have our graylog instance sitting on a server that is not directly reachable from the Internet. In order to allow access to the HTTP GELF input, we defined a ProxyPass on a publicly visible server like so:

    <Location /gelf/>
        ProxyPass http://10.24.0.42:12201/gelf
        ProxyPassReverse http://10.24.0.42:12201/gelf
    </Location>

However, trying to establish a connection to graylog through this using the stream_socket_client as the StreamSocketClient does, always results in a 400 - Bad Request response by the Apache server:

RuntimeException: Graylog-Server didn't answer properly, expected 'HTTP/1.x 202 Accepted', response is 'HTTP/1.1 400 Bad Request Date: Thu, 22 Sep 2016 17:44:48 GMT Server: Apache/2.4.10 (Debian) Content-Length: 445 Connection: close Content-Type: text/html; charset=iso-8859-1' in /var/www/vendor/graylog2/gelf-php/src/Gelf/Transport/HttpTransport.php on line 189

Using curl, there's no issue at all:

kwisatz@mazer:~$ curl -i -XPOST https://graylog.domain.tld/gelf/ -p0 -d '{"short_message":"Hello there", "host":"example.org", "facility":"test", "_foo":"bar"}'
HTTP/1.1 202 Accepted
Date: Thu, 22 Sep 2016 17:15:07 GMT
Server: Apache/2.4.10 (Debian)
Content-Length: 0
Strict-Transport-Security: max-age=15768000
Connection: close

I've been trying to figure out if I had to configure mod_proxy differently for this to work but haven't really found any useful clues. I'm also not familiar with PHP's stream_socket_client and how this works on ssl:// socket connections.

It works just fine when not using SSL, but I'd really prefer not to send this data over the wire in plain-text.

I was hoping someone might have had the same issue already and solved it or someone might know how to configure mod_proxy with Apache?

Or, failure to figure this out, would it be possible to switch out the socketClient for a simple php-curl client?

Fix message-to-array conversion / better support for multiple GELF standards

Tasklist

  • Move message-to-array logic to classes (separate ones for 1.0 and 1.1) SerializerInterface which convert the message to a JsonSerializable structure
  • Move any logic revolving around $version or getVersion from Message to the serializers
  • Move message-validation to serializers

Original issue

There is currently a condition in place to handle 1.0 and 1.1 message serialization. (

if ($this->getVersion() == "1.1") {
)

When the GELF 2.0 spec is finalized, we need a better way to support multiple GELF standards.

Achieving this without breaking BC would be optimal. I'd rather not force GELF 2.0 users to a 2.0 upgrade, but if there is no way around so be it.

Wish: GelfPublisher should have an option to ignore transport errors

I am using the GelfPublisher with UdpTransport in a website combination with Monolog to log to an logstash/elasticsearch server (ELK) and it is working great!

There is only one thing missing:
If a target server is not available the logging attempt terminates with a PHP error.
In this case I would prefer to have my website do its function as before, even if Gelf logging is not available.
This could be configured using an "ignore_error" option.

To avoid the need to do it in every transport it could be done in Publisher::publish if the foreach-loop is changed like this:

foreach ($this->transports as $transport) {
/* @var $transport TransportInterface */

    try {
        $transport->send($message);
    } catch (ExceptionInterface $e) {
        if (!$this->options['ignore_error']) {
            throw new \RuntimeException("Error sending messages to transport", 0, $e);
        }
    }
}

Or, if it is preferred to enable this per transport it could go in AbstractTransport

Fix SSL deprecation warnings

Deprecated: SNI_server_name is deprecated in favor of peer_name in /var/www/vendor/graylog2/gelf-php/src/Gelf/Transport/StreamSocketClient.php on line 153

Deprecated: the 'CN_match' SSL context option is deprecated in favor of 'peer_name' in /var/www/vendor/graylog2/gelf-php/src/Gelf/Transport/StreamSocketClient.php on line 153

See #69

GELF v1.1 compatibility

This library does not work when using against a Graylog2 v0.20 server, because the GELF specification has changed.
http://graylog2.org/gelf#specs

These fields are now deprecated: facility, line, file

Also, there seems to be a error when elasticsearch tries to index fields, that do not conform the the spec. For example, if "line" has the string value "null", the message will be dropped.

Add HTTP-Authentication support to HttpTransport

Securing the GELF-endpoint behind authentication is a common usecase.

We probably do not need a constructor-argument though. An HTTP-Authentication header can be added at each request. It's not required on stream-connect. setAuthentication($user, $password) should be sufficient.

Also only supporting baisc-mode is okay initially, I think.

/cc @steffkes

Get messages

Can this library GET/search messages from Graylog or do we need to go directly to elasticsearch?

How to set $connectTimeout for UdpTransport?

Hi,

is there any way to set $connectTimeout for UdpTransport - which uses StreamSocketClient? Or maybe get access to the $socketClient so we could set it after initializing the UdpTransport?

Thanks,
Patryk

Better support for different versions of Gelf

In gelf-php 1.* the support for GELF 1.0 and 1.1 is hardcoded into Message:toArray.

2.0 should have better support for current and future versions of GELF in regards to architecture, message transformation and serialization.

Decide on a new minimum version

Potential candidates:

  • 7.2: Current active version
  • 7.3: Typehints for properties

Ruled out:

  • Anything 5.x -- even good old PHP5.6 is out of support
  • 7.1: Almost EOL'd and PHP7 users are usually faster to upgrade anyway

Is CHUNK_MAX_COUNT too big?

A message MUST NOT consist of more than 128 chunks.
(c) https://www.graylog.org/resources/gelf/

But I see that your lib allowing up to 256 chunks. Please clarify which is correct.

Also I see logstash logs messages like this:

{
  :timestamp=>"2015-10-14T13:38:01.927000+0300",
  :message=>"Gelfd failed to parse a message skipping",
  :exception=>#<Gelfd::TooManyChunksError: 130 greater than 128>,
  :backtrace=>[
    "/opt/logstash/vendor/bundle/jruby/1.9/gems/gelfd-0.2.0/lib/gelfd/chunked_parser.rb:40:in `parse_chunk'",
    "/opt/logstash/vendor/bundle/jruby/1.9/gems/gelfd-0.2.0/lib/gelfd/chunked_parser.rb:8:in `parse'",
    "/opt/logstash/vendor/bundle/jruby/1.9/gems/gelfd-0.2.0/lib/gelfd/parser.rb:10:in `parse'",
    "/opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-input-gelf-1.0.0/lib/logstash/inputs/gelf.rb:100:in `udp_listener'",
    "/opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-input-gelf-1.0.0/lib/logstash/inputs/gelf.rb:62:in `run'",
    "/opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-core-1.5.4-java/lib/logstash/pipeline.rb:177:in `inputworker'",
    "/opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-core-1.5.4-java/lib/logstash/pipeline.rb:171:in `start_input'"
  ],
  :level=>:warn
}

fwrite return values

Hello,

I use php 5.6 on debian jessy.
I have a problem with TCPTransport.

I establish a connection to tha graylog2 server.
Everything is fine but when an error occurred, for example "Connection reset by peer" or "Broken pipe" - when I shutdown graylog2 infrastructure
fwrite returns with int(0), not boolean false value so "Failed to write to socket" exception will not be thrown because of $byteCount === false condition

Only the the following notice are generated:
Message: fwrite(): send of 166 bytes failed with errno=104 Connection reset by peer
Message: fwrite(): send of 166 bytes failed with errno=32 Broken pipe

Thx,
Cyr

PHP errors for non-string context values

When the log() $context array contains non-string values (such as other arrays), a PHP Notice and PHP Warning are generated as the values are treated as if they will always be strings.

PSR-3 §1.3 states that the context array can contain anything, and the implementation "MUST NOT (...) raise any php error, warning or notice".

It seems that the notice arises from the interpolate() method which is a copy of the FIG example code (shame!) and the warning from a specific implementation in Message::toArray() which filters out non-strings.

If a non-string is encountered it would be good to modify it, such as running arrays through json_encode()

Extensive documentation

Currently the documentation is almost non existent. This should be changed.

  • Explain architecture with comprehensive examples
  • Explain integration into monolog
  • Update README with links to new docs

StreamSocketClient throws exception

Does it make sense that StreamSocketClient::buildSocket method throws exception if it can't connect? I mean if graylog server is down, nobody is catching this exception and my app is no available. Or one of solution would be instead of RuntimeException throw more specific exception that i could catch and handle it.

Named constructor HttpTransport::fromUrl

It'd be a nice addition for HttpTransport to have string-constructor like $transport = HttpTransport::fromUrl("http://logs.example.org:8080/gelf").

  • Use http://php.net/manual/function.parse-url.php
  • http and https should set their port automatically if not specified
  • Maybe allow an optional detailed SslOptions-object as a 2nd paramater (see #24)?

/cc @steffkes

Failed to write to socket: fwrite()

I use this package to send logs via monolog logger to Graylog2 server via UDP. I also use tunelling from my machine to this server, cause I cannot reach it directly. I've configured it with socat based on this example:

http://zarb.org/~gc/html/udp-in-ssh-tunneling.html

Basically, everything works as a charm when the tunel is created.
Although when the tunel is not there, then fwrite raises exception (what's more interesning: every 2nd time):

Exception raised 'Failed to write to socket: fwrite(): send of 361 bytes failed with errno=111 Connection refused (8) File: /var/www/paymentdating/vendor/graylog2/gelf-php/src/Gelf/Transport/StreamSocketClient.php, line# 225 (code 0)

so if my code looks like this:

$myLogger->alert('karamba'); $myLogger->alert('karamba'); $myLogger->alert('karamba'); $myLogger->alert('karamba');

The error is raised only for 2nd and 4th call.
I thought that because of UDP protocol there will be no exceptions. In the end I like them even, cause I can check the Graylog server is unreachable, although in most cases the script is gonna log only once and then unfortunately no exception is raised by fwrite.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.