0

In Drupal 7 hook_exit is implemented in the Statistics module to gather statistics for page accesses. This change record suggest the equivalent in Drupal 8 is to listen for kernel.terminate events. Here's my code:

/mymodule/mymodule.services.yml

services:
  mymodule.page_exit:
    class: Drupal\mymodule\EventSubscriber\PageExit
    tags:
      - {name: event_subscriber}

/mymodule/src/Event/Subscriber/PageExit.php

namespace Drupal\mymodule\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;

class PageExit implements EventSubscriberInterface {

  /**
   * {@inheritdoc}
   */
  static function getSubscribedEvents() {
    $events[KernelEvents::TERMINATE][] = array('logAccess');
    return $events;
  }

  /**
   * Call this method whenever the KernelEvents::TERMINATE event is dispatched.
   */
  public function logAccess(PostResponseEvent $event) {

    // $access_info = $event->getRequest()

    \Drupal::database()->insert('mymodule_table')
      ->fields(array(
        'hostname' => '?',
        'referrer_url' => '?',
        'timestamp' => REQUEST_TIME,
      ))
      ->execute();
  }
}

Currently, for a single page request, the method logAccess gets called five times. I'm interested in logging the hostname and referrer url of my page visitor, just once.

In essence, I'm trying to port some of this this Drupal 7 code from statistics_exit in statistics.module:

drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);

// For anonymous users unicode.inc will not have been loaded.
include_once DRUPAL_ROOT . '/includes/unicode.inc';
// Log this page access.
db_insert('accesslog')
  ->fields(array(
    // 'title' => truncate_utf8(strip_tags(drupal_get_title()), 255),
    // 'path' => truncate_utf8($_GET['q'], 255),
    'url' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
    'hostname' => ip_address(),
    // 'uid' => $user->uid,
    // 'sid' => session_id(),
    // 'timer' => (int) timer_read('page'),
    'timestamp' => REQUEST_TIME,
  ))
  ->execute();

Any ideas?

5
  • 1
    I think the reason for getting 5 entries is because there are requests on the background(js, css or most likely image style generated images and they go through the index file causing subrequests). So you have to check the type of the response and/or route and do a log afterwards. For example this is my counter gist.github.com/ivanjaros/211e499b76385565c7b652741e669565 – user21641 Jul 19 '17 at 7:12
  • statistics module in 8.x actually uses a JS file that triggers a POST request to the server, that is the only thing that also works with external caches like Varnish/Fastly and so on. – Berdir Jul 19 '17 at 22:37
  • That same mechanism is used in 7.x for counting nodes, but not for logging access statistics, right? In 7.x they are logged within an implementation of hook_exit (line 82 of statistics.module). Is using hook_exit for this purpose in 7.x unreliable? – dbj44 Jul 20 '17 at 8:51
  • Did you even had a look at the suggestion below? It seems like you are still trying to solve the same problem => drupal.stackexchange.com/questions/243308/… – Stefanos Petrakis Aug 11 '17 at 23:18
  • Thanks - I did see your reply. I decided not to use an event subscriber in the end because of the comment by @Berdir. My code could be used on an environment that uses external caching, so I decided it wasn't the way to go. – dbj44 Aug 12 '17 at 10:36
0

The comments you got are golden, my answer is along the lines of what @IvanJaros suggested, but follows a simpler approach. And take care to check the case of external caching (if any is in play).

The following should make your current approach work.

public function logAccess(PostResponseEvent $event) {

  $request_format = $event->getRequest()->getRequestFormat();

  if ($request_format === 'html') {
    // \Drupal::database()->
  }
  else {
    // Do other stuff.
  }
  ...

This snippet tries to detect requests with the html format (i.e. page requests) and will ignore other ones, like e.g. application/json.

Good luck!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.