Skip to content
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

Implemented logging to Graylog server #14847

Closed
wants to merge 8 commits into from

Conversation

Faldon
Copy link
Contributor

@Faldon Faldon commented Mar 25, 2019

For an old feature request, I implemented logging as GELF to a Graylog server.

Features

  • Configurable to use tcp or udp in system config
  • Auto-chunking udp messages larger than 1024 bytes
  • Log server and port configurable in host:port notation

Implementation

Added a new class handling writing the logs to the configured server through a socket connection. If the connection fails, it returns silently.
GELF supports chunking of UDP messages up to 128 chunks. So a log message can be approx. 126kB in size or it is silently discarded.
I also added the ability to configure the logging method through the occ log:manage command scope.

Closes #95

@Faldon Faldon added the 3. to review Waiting for reviews label Mar 25, 2019
@Faldon
Copy link
Contributor Author

Faldon commented Mar 26, 2019

Considering the CI check... Shall / must I rebase my branch onto master?

@rullzer rullzer added this to the Nextcloud 17 milestone Mar 26, 2019
@kesselb
Copy link
Contributor

kesselb commented Mar 26, 2019

Considering the CI check... Shall / must I rebase my branch onto master?

The autoloaders are not up to date
Please run: bash build/autoloaderchecker.sh
And commit the result

Added the appropriate logging class.
Provided the necessary system config examples.
Added loading the log class from the LogFactory.
Extended the LogFactory unit tests to include the graylog class.
Added Graylog test class skeleton.

Signed-off-by: Thomas Pulzer <t.pulzer@thesecretgamer.de>
Changed method of how to connect to remote server for writing logs.
Wrote unit tests for chunked udp, unchunked udp and tcp connections.

Signed-off-by: Thomas Pulzer <t.pulzer@thesecretgamer.de>
Fixed wrong byte packing when sending chunked UDP packets.
Improved test code and comments.

Signed-off-by: Thomas Pulzer <t.pulzer@thesecretgamer.de>
Signed-off-by: Thomas Pulzer <t.pulzer@thesecretgamer.de>
…l with occ

Signed-off-by: Thomas Pulzer <t.pulzer@thesecretgamer.de>
Signed-off-by: Thomas Pulzer <t.pulzer@thesecretgamer.de>
This revealed former incorrect encoding of field 'level' (is nuber, but
was encoded as string. Therefore, the test method had to be adjusted as
well.

Signed-off-by: Thomas Pulzer <t.pulzer@thesecretgamer.de>
Signed-off-by: Thomas Pulzer <t.pulzer@thesecretgamer.de>
@Faldon
Copy link
Contributor Author

Faldon commented Mar 28, 2019

Rebased onto master to have all the checks passed.

@MorrisJobke MorrisJobke mentioned this pull request Jul 15, 2019
28 tasks
Copy link
Contributor

@kesselb kesselb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the great contribution 👍 Please excuse the late code review 😞

@@ -0,0 +1,109 @@
<?php
/**
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*

@@ -0,0 +1,109 @@
<?php
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<?php
<?php
declare(strict_types=1);

@@ -0,0 +1,147 @@
<?php
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<?php
<?php
declare(strict_types=1);

@@ -0,0 +1,147 @@
<?php
/**
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*

$address = $config->getSystemValue('graylog_host', '');
if (false !== strpos($address, ':')) {
$this->target = explode(':', $address)[0];
$this->port = intval(explode(':', $address)[1]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$this->port = intval(explode(':', $address)[1]);
$this->port = (int) explode(':', $address)[1];

$host = $this->config->getSystemValue('graylog_host');
$output->writeln('Log server: '.$host);
$protocol = $this->config->getSystemValue('graylog_proto', 'udp');
$output->writeln('Connection protocol: '.$protocol);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$output->writeln('Connection protocol: '.$protocol);
$output->writeln('Connection protocol: ' . $protocol);

* @throws \InvalidArgumentException
*/
protected function isBackendGraylogSet($new=null) {
$old = $this->config->getSystemValue('log_type', self::DEFAULT_BACKEND);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this 🤔 It's not possible to set greylog_host or greylog_proto if the current log_type or the new log_type is not greylog. That makes sense but is very strict. There is no harm if greylog_host or greylog_proto are set with another log_type. I think we need some input from another reviewer.

@@ -99,6 +112,20 @@ protected function execute(InputInterface $input, OutputInterface $output) {
$toBeSet['logtimezone'] = $timezone;
}

if ($host = $input->getOption('host')) {
array_key_exists('log_type', $toBeSet) ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to suggest to change this a bit. Its quite hard to read and we have the same logic two times. The isBackendGraylogSet method should accept an array $toBeSet and do the array_key_exists check.

public function __construct(IConfig $config) {
$this->host = gethostname();
$this->protocol = $config->getSystemValue('graylog_proto', 'udp');
$address = $config->getSystemValue('graylog_host', '');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code is fine. I would pick a slightly different approach but everyone has a different code style so this is up to you.

		$address = explode(':', $config->getSystemValue('graylog_host'));
		if (count($address) === 2) {
			$this->target = $address[0];
			$this->port = (int)$address[1];
		} else {
			$this->target = $address[0];
			$this->port = 514;
		}

* @param string $new
* @throws \InvalidArgumentException
*/
protected function isBackendGraylogSet($new=null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should find another name for it.

$msg = json_encode([
'version' => self::$VERSION,
'host' => $this->host,
'short_message' => '{'.$app.'} '.$message,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to use the new way here as well:

Suggested change
'short_message' => '{'.$app.'} '.$message,
'short_message' => $this->logDetailsAsJSON($app, $message, $level),

Was added just now via #16416

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe the non-encoded one is better here: logDetails(), because it does not return a string with JSON, but an object with keys.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if Graylog can handle nested data in the short_message key that should be the way to go.

@rullzer rullzer modified the milestones: Nextcloud 17, Nextcloud 18 Aug 8, 2019
@rullzer
Copy link
Member

rullzer commented Aug 8, 2019

Lets merge this early in 18.
@juliushaertl could you help out since you touched the logging code recently

@juliusknorr juliusknorr self-assigned this Aug 8, 2019
This was referenced Dec 11, 2019
@ChristophWurst
Copy link
Member

Lot's of unaddressed review comments. Some unanswered questions. This needs triage. Not assigning to 19 because pushing this from release to release has no point.

@ChristophWurst ChristophWurst removed the 3. to review Waiting for reviews label Dec 13, 2019
@ChristophWurst ChristophWurst added the 0. Needs triage Pending check for reproducibility or if it fits our roadmap label Dec 13, 2019
@ChristophWurst ChristophWurst removed this from the Nextcloud 18 milestone Dec 13, 2019
@ChristophWurst
Copy link
Member

What might make more sense IMO is to use some existing packages like https://packagist.org/packages/giacomofurlan/php-graylog-gelf in combination with #18200. And preferably make this an app.

Like

  • Use PSR for the logger interface
  • Make the logger implementation pluggable
  • Create an implementation for Graylog as an app

@ChristophWurst
Copy link
Member

Let's close this due to lack of activity. If anyone would like to bring this feature to life, please do so as an app as described in my previous comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
0. Needs triage Pending check for reproducibility or if it fits our roadmap enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add new log_type GELF
6 participants