Захотелось сделать свой собственный счетчик посетителей и хитов для своего сайта генерируемого Hugo для темы HBS. Собирать счетчик буду на основе логов веб сервера, обработанных GoAccess. Будет добавлен блок в боковую панель сайта со статистикой в виде смешанного графика количества посетителей за 30 дней.

Конфиг GoAccess

Краткий конфиг без лишней воды, только то что надо без всяких пояснений. Если нужны данные расшифровки строк конфига их можно прочитать тут - файл конфигурации goaccess. Поэтому создадим конфиг, который будет формировать краткий отчет, без лишних параметров.

sudo nano /etc/goaccess/stat-wildserver.ru.conf

Вставить данные опции:

######################################
# Опции формата времени (обязательно)
######################################

time-format %H:%M:%S

######################################
# Опции формата даты (обязательно)
######################################

date-format %d/%b/%Y

######################################
# Опции формата логов
######################################

log-format COMBINED

######################################

config-dialog false 

hl-header true

json-pretty-print false

no-color false

no-column-names false

no-csv-summary false

no-progress false

no-tab-scroll false

with-mouse false

agent-list false

with-output-resolver false

# Написать например IP адреса своих устройств, чтобы не учитывались ваши заходы
#exclude-ip 127.0.0.1
#exclude-ip 192.168.0.1-192.168.0.100

http-method yes

http-protocol yes

no-query-string false

no-term-resolver false

444-as-404 false

4xx-to-unique-count false

all-static-files false

# Включить дополнительный разделенный список браузеров/краулеров/ленточек и т.д.
# См. config/browsers.list для примера или
# https://raw.githubusercontent.com/allinurl/goaccess/master/config/browsers.list
#
#browsers-file <filename>

double-decode false

ignore-crawlers true

crawlers-only false

unknowns-as-crawlers false

# Игнорировать парсинг и отображение указанной панели.
#ignore-panel VISITORS
ignore-panel REQUESTS
ignore-panel REQUESTS_STATIC
ignore-panel NOT_FOUND
ignore-panel HOSTS
ignore-panel OS
ignore-panel BROWSERS
ignore-panel VISIT_TIMES
ignore-panel VIRTUAL_HOSTS
ignore-panel REFERRERS
ignore-panel REFERRING_SITES
ignore-panel KEYPHRASES
ignore-panel STATUS_CODES
ignore-panel REMOTE_USER
ignore-panel CACHE_STATUS
ignore-panel GEO_LOCATION
ignore-panel MIME_TYPE
ignore-panel TLS_TYPE

Сохраняем файл Ctrl+O , Enter, и закрываем Ctrl+X

Логи сайта

Ранее перенес логи сайта в отдельный каталог для удобства их ротации. Что бы можно было регулировать необходимый промежуток времени хранения логов для последующего формирования отчетов. Про это писал тут - ротация логов для конкретного сайта на примере apache2

Создание отчета по сайту

Создать исполняемый файл nano ~/stat/create-report-wildserver.ru.sh, который будет запускаться например раз в час для объединения всех логов даже которые заархивированы logrotate. После обрабатываться GoAccess, который сформирует файл статистики в формате .json, для более удобной обработки и передачи на сайт.

#!/bin/bash

   # Пути к файлам логов и отчету с конфигом
   LOG_DIR="/var/log/apache2/wildserver.ru"
   ALL_LOG="/home/USER/stat/wildserver.ru.access.log"
   REPORT="/home/USER/wwww/wildserver.ru/stat/stat.json"
   CONFIG="/etc/goaccess/stat-wildserver.ru.conf"

   # Создание или очистка общего файла логов
   > $ALL_LOG

   # Объединение всех файлов логов (обычных и архивированных) в один
   # Добавление обычного лог-файла
   cat $LOG_DIR/wildserver.ru.access.log >> $ALL_LOG

   # Добавление дневного лога
   cat $LOG_DIR/wildserver.ru.access.log.1 >> $ALL_LOG

   # Добавление всех архивов
   for file in $LOG_DIR/wildserver.ru.access.log.*.gz; do
       if [ -f "$file" ]; then
           zcat "$file" >> $ALL_LOG
       fi
   done

   # Создание HTML-отчета GoAccess
   goaccess $ALL_LOG -o $REPORT --json -p $CONFIG

   # Изменение прав на отчет для пользователя USER и группы www-data
   chown USER:www-data $REPORT

Сохраняем файл Ctrl+O , Enter, и закрываем Ctrl+X

Сделать скрипт исполняемым:

chmod +x ~/stat/create-report-wildserver.ru.sh

Добавить задачу в сron

Файл должен исполняться от имени root

sudo crontab -e

0 * * * * /home/USER/stat/create-report-wildserver.ru.sh

Скрипт будет запускаться каждый час.

Вставка кода статистики в сайт

Открыть проект с сайтом в Visual Studio Code создать пользовательский файл layouts/partials/sidebar/profile.html в котором добавим блок сайдбара с графиком посещения пользователей в стиле темы HBS:

{{- $author := site.Params.author }}
{{- with site.Author }}
  {{- $author = . }}
  {{- warnf "The author key in site configuration is deprecated. Use params.author instead." }}
{{- end }}
{{- if $author }}
{{- $layout := default "" $author.params.layout }}
{{- if eq $layout "compact" }}
  {{- partial "sidebar/profile/compact" . }}
{{- else }}
  {{- partial "sidebar/profile/default" . }}
{{- end }}
{{- end }}

{{- $collapsed := default false .Site.Params.sidebar.collapsed }}
<div class="accordion statistics">
  <section class="row card component">
    <div class="card-header">
      <h4 class="card-title text-surface">
        <span>Статистика за 30 дней</span>
      </h4>
      <a
        class="accordion-button d-lg-none mb-1 shadow-none p-0 bg-transparent{{ if $collapsed }} collapsed{{ end }}"
        role="button" data-bs-toggle="collapse" href="#statistics" aria-expanded="{{ if $collapsed }}false{{ else }}true{{ end }}" aria-controls="statistics">
        Статистика
      </a>
    </div>
    <div class="card-body collapse accordion-collapse accordion-body d-lg-block{{ if not $collapsed }} show{{ end }}" id="statistics">
      <!-- Блок для графика -->
      <canvas id="statsChart" width="800" height="300"></canvas>
    </div>
  </section>
</div>

<!-- Интеграция Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<script>
  fetch('/stat/stat.json') // Обновите путь до статического JSON
    .then(response => {
      if (!response.ok) {
        throw new Error('Сетевая ошибка: ' + response.status);
      }
      return response.json();
    })
    .then(data => {
      // Сортируем даты от более ранних к более поздним
      const entries = data.visitors.data.sort((a, b) => {
        const dateA = new Date(a.data.slice(0, 4), a.data.slice(4, 6) - 1, a.data.slice(6, 8));
        const dateB = new Date(b.data.slice(0, 4), b.data.slice(4, 6) - 1, b.data.slice(6, 8));
        return dateA - dateB;
      });

      // Функция для получения сокращенного названия дня недели
      const getDayShortName = (date) => {
        const days = ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'];
        return days[date.getDay()];
      };

      const labels = entries.map(entry => {
        const date = new Date(entry.data.slice(0, 4), entry.data.slice(4, 6) - 1, entry.data.slice(6, 8));
        const formattedDate = `${date.getDate().toString().padStart(2, '0')}.${(date.getMonth() + 1).toString().padStart(2, '0')}.${date.getFullYear()}`;
        const dayShortName = getDayShortName(date);
        return `${formattedDate} (${dayShortName})`;
      });

      const visitorsCounts = entries.map(entry => entry.visitors.count);

      // Настройка графика
      const ctx = document.getElementById('statsChart').getContext('2d');
      const statsChart = new Chart(ctx, {
        type: 'bar',
        data: {
          labels: labels,
          datasets: [
            {
              type: 'bar',
              label: 'Посетители',
              data: visitorsCounts,
              backgroundColor: 'rgba(75, 192, 192, 0.5)',
              borderColor: 'rgba(75, 192, 192, 1)',
              borderWidth: 1
            },
            {
              type: 'line',
              label: 'Линия посетителей',
              data: visitorsCounts,
              fill: true, // Включаем заливку
              backgroundColor: 'rgba(255, 99, 132, 0.2)', // Цвет заливки
              borderColor: 'rgba(255, 99, 132, 1)',
              tension: 0.4, // Уровень сглаживания линии
              pointRadius: 0 // Убираем точки
            }
          ]
        },
        options: {
          responsive: true,
          scales: {
            x: {
              display: false // Скрыть метки на оси X
            },
            y: {
              beginAtZero: true
            }
          },
          plugins: {
            legend: {
              display: false // Убираем отображение легенды
            },
            tooltip: {
              callbacks: {
                title: (tooltipItems) => {
                  const index = tooltipItems[0].dataIndex;
                  return labels[index];
                },
                label: (tooltipItem) => {
                  const value = tooltipItem.raw;
                  return `Посетителей: ${value}`;
                }
              }
            }
          }
        }
      });
    })
    .catch(error => {
      console.error('Ошибка:', error);
    });
</script>

Для локального тестирования разместить файл сгенерированный скриптом create-report-wildserver.ru.sh в каталог static/stat/stat.json

Итог

Наблюдаем симпатишный счетчик посетителей отлично вписавшийся в дизайн сайта. С данными статистики надо будет еще разобрацо, точнее с конфигом GoAccess. Данные разнятся с гугловскими, кто то из них пиждит одназначно, меня конечно радуют большие цифры посещений, которые выдает мой счетчик. Хоть в настройках и сделал так что бы не учитывались заходы пауков, но надо будет еще добавить кастомный лист. В конфиге среди комментариев была ссылочка на дополнительный лист https://raw.githubusercontent.com/allinurl/goaccess/master/config/browsers.list надо изучить поюзать тему. В целом я доволен проделанной работой.