--- /dev/null
+<?php
+/**
+ * dolphin. Collection of useful PHP skeletons.
+ * Copyright (C) 2024 Johannes 'Banana' Keßler
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
+ *
+ * You should have received a copy of the
+ * COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+ * along with this program. If not, see http://www.sun.com/cddl/cddl.html
+ */
+
+/**
+ * This is an exmaple to show how to send custom logs/metrics
+ * to loki and use them in grafana
+ */
+
+# basic loki endpoint information
+const LOKI_HOST = "localhost";
+const LOKI_PORT = 3100;
+const LOKI_PUSH_API = "/loki/api/v1/push";
+# loki does not have protection
+# https://grafana.com/docs/loki/latest/operations/authentication/
+# this example does use basic auth
+# if you do not use it, comment both constants
+const LOKI_USER = "name";
+const LOKI_USER_PW = "pass";
+
+# Loki
+require_once 'loki.php';
+
+# see https://grafana.com/docs/loki/latest/reference/loki-http-api/#ingest-logs for
+# data structure
+$Loki = new Loki(LOKI_HOST, LOKI_PORT, array("streamlabel1" => "value1", "streamlabel2" => "value2"));
+
+$Loki->log("log line text", array("structuredData" => "value"));
+$Loki->log("log line text", array("structuredData" => "value2"));
+
+# send the data to loki at the end to avoid and multiple calls and breaking
+# the process of the script.
+$Loki->send();
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * dolphin. Collection of useful PHP skeletons.
+ * Copyright (C) 2024 Johannes 'Banana' Keßler
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
+ *
+ * You should have received a copy of the
+ * COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+ * along with this program. If not, see http://www.sun.com/cddl/cddl.html
+ */
+
+
+/**
+ * Send log messages with optional structured data to Loki using the http api
+ * https://grafana.com/docs/loki/latest/reference/loki-http-api/
+ */
+class Loki {
+
+ /**
+ * @var string The host Loki runs on
+ */
+ private string $_host;
+
+ /**
+ * @var int The port number from the Loki installation
+ */
+ private int $_port;
+
+ /**
+ * @var array The stream lables. See Loki http api
+ */
+ private array $_stream = array();
+
+ /**
+ * @var array The log messages as an array to be send with send()
+ */
+ private array $_values = array();
+
+ /**
+ * @var string basic auth for endpoint
+ */
+ private string $_auth;
+
+ /**
+ * @param string $host Loki host
+ * @param int $port Loko host port
+ * @param array $stream Stream array with labels to send with
+ */
+ public function __construct(string $host, int $port, array $stream) {
+ $this->_stream = $stream;
+ $this->_host = $host;
+ $this->_port = $port;
+
+ if(defined("LOKI_USER") && !empty(LOKI_USER)) {
+ $this->_auth = base64_encode(LOKI_USER.":".LOKI_USER_PW);
+ }
+ }
+
+ /**
+ * Add a log message with optional structured data to the values to be send()
+ *
+ * @param string $msg The log message
+ * @param array $structuredData Additional structured data to be processed from Loki if configured
+ * @return void
+ */
+ public function log(string $msg, array $structuredData=array()): void {
+ $_nanosec = strval(shell_exec("date +%s%9N")-1);
+ if(!empty($structuredData)) {
+ $this->_values[] = array($_nanosec, $msg, $structuredData);
+ } else {
+ $this->_values[] = array($_nanosec, $msg);
+ }
+ }
+
+ /**
+ * Send the collected messages from log() to the loki installation
+ * The messages to be send will be resetted after
+ *
+ * @return string Non empty on error
+ */
+ public function send(): string {
+ $ret = "";
+
+ if(!LOKI_ENABLE) return $ret;
+
+ $data = array(
+ "streams" => array(
+ array(
+ "stream" => $this->_stream,
+ "values" => $this->_values
+ )
+ )
+ );
+
+ $data = json_encode($data);
+ $out = "POST ".LOKI_PUSH_API." HTTP/1.1\r\n";
+ $out .= "Host: $this->_host\r\n";
+ if(!empty($this->_auth)) {
+ $out .= "Authorization: Basic $this->_auth\r\n";
+ }
+ $out .= "Content-Type: application/json\r\n";
+ $out .= "Content-Length: ".strlen($data)."\r\n";
+ $out .= "Connection: Close\r\n\r\n";
+
+ $fp = fsockopen($this->_host, $this->_port, $errno, $errstr, 5);
+ if($fp) {
+ fwrite($fp, $out);
+ fwrite($fp, $data."\r\n");
+ fclose($fp);
+ } else {
+ $ret = $errno.' '.$errstr;
+ }
+
+ # reset
+ $this->_values = array();
+
+ return $ret;
+ }
+}