Sentry: Browser not correctly recognized from the User-Agent header

Created on 25 May 2018  路  12Comments  路  Source: getsentry/sentry

On Sentry.io I am seeing the following interface:

image

However when looking at the JSON for that event (it only has a single event) I can see the User-Agent is sent and being stored.

image

I would assume that would extract the browser correctly. I have a lot of other User-Agents also not being recognized, I have yet to see a correct one being recognized.

Language: PHP

(Noticed the icons are round now since I took the screenshots yesterday but that has not changed the detection)

Most helpful comment

Yes, however I would like to not have the user or sentry-php parse the user agent string if possible. So it would be nice if there is a way we can let sentry decide about the User-Agent stuff but still keep the php version context.

All 12 comments

This only happens if the contexts fields is preset as well.
If the contexts field is omitted, the browser is detected correctly (with the same User-Agent).

In the PHP SDK we sent the PHP version in the contexts.runtime is there a way to still sent the PHP version? But let Sentry decide the browser / OS?

Can also confirm I'm getting these. Disabling the 'runtime' context shows the correct operating system and OS as a tag, but not in the 'big three' of "user, browser, runtime" at the top. Disabling runtime causes this bar you've got in your first screenshot to disappear completely.

Re-enabling runtime context brings the bar back, yet the 'Browser' still shows as 'Unknown' despite displaying a valid User-Agent header.

I've managed to display OS and browser info by manually adding $data['contexts'] entries:
image
It is not documented, but array of data sent to Sentry should contains entries like this under context index to has browser/OS/runtime info properly displayed:

<?php
$data['contexts'] = [
    'runtime' => [
        'version' => '7.2.7',
        'name' => 'php'
    ],
    'os' => [
        'name' => 'Linux'
    ],
    'browser' => [
        'version' => '62.0',
        'name' => 'Firefox'
    ]
];

I've managed to achieve this by some dirty-fix. First, I am adding required data in custom processor:

class SessionRequestProcessor {
   ...
    public function process(array $record) {
        if (null !== $this->browserVersion) {
            $record['contexts']['browser'] = [
                'version' => $this->browserVersion,
                'name' => $this->browserName,
            ];

            $record['contexts']['os'] = ['name' => $this->osName];
        }
    }
    ...
}

then, I've modified write method in Monolog\Handler\RavenHandler (I am using Monolog approach) to add also contexts index to record:

        if (!empty($record['contexts'])) {
            $options['contexts'] = $record['contexts'];
        }

I am going to add PR for this changes.

Yes, however I would like to not have the user or sentry-php parse the user agent string if possible. So it would be nice if there is a way we can let sentry decide about the User-Agent stuff but still keep the php version context.

馃憤 Same issue here

+1

Same here!

Faced the same problem. I followed the instructions from @mleczakm but improved the following part:

then, I've modified write method in Monolog\Handler\RavenHandler (I am using Monolog approach) to add also contexts index to record:

        if (!empty($record['contexts'])) {
            $options['contexts'] = $record['contexts'];
        }

Instead of that I used a more neat approach and created my own class that extends Monolog\Handler\RavenHandler and correctly processes the contexts field:

use Monolog\Logger;

class DFYRavenHandler extends Monolog\Handler\RavenHandler
{
    /**
     * Translates Monolog log levels to Raven log levels.
     */
    protected $logLevels = array(
        Logger::DEBUG     => Raven_Client::DEBUG,
        Logger::INFO      => Raven_Client::INFO,
        Logger::NOTICE    => Raven_Client::INFO,
        Logger::WARNING   => Raven_Client::WARNING,
        Logger::ERROR     => Raven_Client::ERROR,
        Logger::CRITICAL  => Raven_Client::FATAL,
        Logger::ALERT     => Raven_Client::FATAL,
        Logger::EMERGENCY => Raven_Client::FATAL,
    );

    /**
     * {@inheritdoc}
     */
    public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true)
    {
        parent::__construct($ravenClient, $level, $bubble);
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        $previousUserContext = false;
        $options = array();
        $options['level'] = $this->logLevels[$record['level']];
        $options['tags'] = array();
        if (!empty($record['extra']['tags'])) {
            $options['tags'] = array_merge($options['tags'], $record['extra']['tags']);
            unset($record['extra']['tags']);
        }
        if (!empty($record['context']['tags'])) {
            $options['tags'] = array_merge($options['tags'], $record['context']['tags']);
            unset($record['context']['tags']);
        }
        if (!empty($record['context']['fingerprint'])) {
            $options['fingerprint'] = $record['context']['fingerprint'];
            unset($record['context']['fingerprint']);
        }
        if (!empty($record['context']['logger'])) {
            $options['logger'] = $record['context']['logger'];
            unset($record['context']['logger']);
        } else {
            $options['logger'] = $record['channel'];
        }
        foreach ($this->getExtraParameters() as $key) {
            foreach (array('extra', 'context') as $source) {
                if (!empty($record[$source][$key])) {
                    $options[$key] = $record[$source][$key];
                    unset($record[$source][$key]);
                }
            }
        }
        if (!empty($record['context'])) {
            $options['extra']['context'] = $record['context'];
            if (!empty($record['context']['user'])) {
                $previousUserContext = $this->ravenClient->context->user;
                $this->ravenClient->user_context($record['context']['user']);
                unset($options['extra']['context']['user']);
            }
        }
        if (!empty($record['extra'])) {
            $options['extra']['extra'] = $record['extra'];
        }
        if (!empty($record['contexts'])) {
            $options['contexts'] = $record['contexts'];
            unset($record['contexts']);
        }

        if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) {
            $options['extra']['message'] = $record['formatted'];
            $this->ravenClient->captureException($record['context']['exception'], $options);
        } else {
            $this->ravenClient->captureMessage($record['formatted'], array(), $options);
        }

        if ($previousUserContext !== false) {
            $this->ravenClient->user_context($previousUserContext);
        }
    }
}

I see the PR that @mleczakm created, but then he closed it himself. It's a pity. It could be a good contribution for all users!

I added a new PR. It allows to use contexts both inside processors and in direct log calls like addNotice functions. You can vote and comment this PR here.

fyi @HazAT is working on this.

Was this page helpful?
0 / 5 - 0 ratings