getsentry / sentry

Developer-first error tracking and performance monitoring
https://sentry.io
Other
38.95k stars 4.18k forks source link

Browser not correctly recognized from the User-Agent header #8555

Closed stayallive closed 5 years ago

stayallive commented 6 years ago

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)

martinstuecklschwaiger commented 6 years ago

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).

stayallive commented 6 years ago

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?

dkmonaghan commented 6 years ago

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.

mleczakm commented 6 years ago

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.

stayallive commented 6 years ago

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.

JoshZA commented 6 years ago

👍 Same issue here

smknstd commented 6 years ago

+1

andreshg112 commented 6 years ago

Same here!

rvalitov commented 5 years ago

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!

rvalitov commented 5 years ago

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.

untitaker commented 5 years ago

fyi @HazAT is working on this.

HazAT commented 5 years ago

ref: https://github.com/getsentry/sentry/pull/10679