Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/php/web/Application.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* @test xp://web.unittest.ApplicationTest
*/
abstract class Application implements \lang\Value {
private $routing;
private $routing= null;
protected $environment;

/**
Expand Down
9 changes: 7 additions & 2 deletions src/main/php/web/Environment.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* @test xp://web.unittest.EnvironmentTest
*/
class Environment {
private $profile, $webroot, $docroot, $arguments;
private $profile, $webroot, $docroot, $arguments, $logging;
private $sources= [];

/**
Expand All @@ -26,8 +26,9 @@ class Environment {
* @param string|io.Path $docroot
* @param (string|util.PropertySource)[] $config
* @param string[] $arguments
* @param string|string[]|web.Logging $logging Defaults to logging to console
*/
public function __construct($profile, $webroot, $docroot, $config, $arguments= []) {
public function __construct($profile, $webroot, $docroot, $config, $arguments= [], $logging= '-') {
$this->profile= $profile;
$this->webroot= $webroot instanceof Path ? $webroot : new Path($webroot);
$this->docroot= $docroot instanceof Path ? $docroot : new Path($docroot);
Expand All @@ -40,6 +41,7 @@ public function __construct($profile, $webroot, $docroot, $config, $arguments= [
$this->sources[]= new ResourcePropertySource($source);
}
}
$this->logging= $logging instanceof Logging ? $logging : Logging::of($logging);
$this->arguments= $arguments;
}

Expand All @@ -52,6 +54,9 @@ public function webroot() { return $this->webroot; }
/** @return io.Path */
public function docroot() { return $this->docroot; }

/** @return web.Logging */
public function logging() { return $this->logging; }

/**
* Gets properties
*
Expand Down
81 changes: 81 additions & 0 deletions src/main/php/web/Logging.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php namespace web;

use web\logging\Sink;
use web\logging\ToAllOf;

class Logging {
private $sink;

/**
* Create an instance with a given sink
*
* @param ?web.log.Sink $sink
*/
public function __construct(Sink $sink= null) {
$this->sink= $sink;
}

/** @return ?web.log.Sink */
public function sink() { return $this->sink; }

/**
* Create an instance from a given command line argument
*
* @param string $arg
* @return self
*/
public static function of($arg) {
return new self(Sink::of($arg));
}

/**
* Pipe to a given sink
*
* @param var $sink
* @return self
*/
public function pipe($sink) {
if (null === $sink || $sink instanceof Sink) {
$this->sink= $sink;
} else {
$this->sink= Sink::of($sink);
}
return $this;
}

/**
* Tee to a given sink
*
* @param var $sink
* @return self
*/
public function tee($sink) {
if (null === $this->sink) {
$this->pipe($sink);
} else {
$this->sink= new ToAllOf($this->sink, $sink);
}
return $this;
}

/**
* Writes a log entry
*
* @param web.Request $response
* @param web.Response $response
* @param ?web.Error $error Optional error
* @return void
*/
public function log($request, $response, $error= null) {
$this->sink && $this->sink->log($request, $response, $error);
}

/**
* Returns logging target
*
* @return string
*/
public function target() {
return $this->sink ? $this->sink->target() : '(no logging)';
}
}
50 changes: 50 additions & 0 deletions src/main/php/web/logging/Sink.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php namespace web\logging;

use util\log\LogCategory;

/**
* Base class for all log sinks
*
* @test xp://web.unittest.logging.SinkTest
*/
abstract class Sink {

/**
* Writes a log entry
*
* @param web.Request $response
* @param web.Response $response
* @param ?web.Error $error Optional error
* @return void
*/
public abstract function log($request, $response, $error);

/** @return string */
public function target() { return nameof($this); }

/**
* Factory method from various sources
*
* @param var $arg
* @return ?self
*/
public static function of($arg) {
if ('-' === $arg) {
return new ToConsole();
} else if (null === $arg) {
return null;
} else if (is_callable($arg)) {
return new ToFunction($arg);
} else if (is_array($arg)) {
switch (sizeof($arg)) {
case 0: return null;
case 1: return self::of($arg[0]);
default: return new ToAllOf(...$arg);
}
} else if ($arg instanceof LogCategory) {
return new ToCategory($arg);
} else {
return new ToFile($arg);
}
}
}
53 changes: 53 additions & 0 deletions src/main/php/web/logging/ToAllOf.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php namespace web\logging;

/**
* Log sink which logs to all given sinks
*
* @test xp://web.unittest.logging.ToAllOfTest
*/
class ToAllOf extends Sink {
private $sinks= [];

/**
* Creates a sink writing to all given other sinks
*
* @param (web.log.Sink|util.log.LogCategory|function(web.Request, web.Response, string): void)... $arg
*/
public function __construct(... $args) {
foreach ($args as $arg) {
if ($arg instanceof self) {
$this->sinks= array_merge($this->sinks, $arg->sinks);
} else if ($arg instanceof parent) {
$this->sinks[]= $arg;
} else {
$this->sinks[]= parent::of($arg);
}
}
}

/** @return web.log.Sink[] */
public function sinks() { return $this->sinks; }

/** @return string */
public function target() {
$s= '';
foreach ($this->sinks as $sink) {
$s.= ' & '.$sink->target();
}
return '('.substr($s, 3).')';
}

/**
* Writes a log entry
*
* @param web.Request $response
* @param web.Response $response
* @param ?web.Error $error Optional error
* @return void
*/
public function log($request, $response, $error) {
foreach ($this->sinks as $sink) {
$sink->log($request, $response, $error);
}
}
}
32 changes: 32 additions & 0 deletions src/main/php/web/logging/ToCategory.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php namespace web\logging;

class ToCategory extends Sink {
private $cat;

/** @param util.log.LogCategory $cat */
public function __construct($cat) {
$this->cat= $cat;
}

/** @return string */
public function target() { return nameof($this).'('.$this->cat->toString().')'; }

/**
* Writes a log entry
*
* @param web.Request $response
* @param web.Response $response
* @param ?web.Error $error Optional error
* @return void
*/
public function log($request, $response, $error) {
$query= $request->uri()->query();
$uri= $request->uri()->path().($query ? '?'.$query : '');

if ($message) {
$this->cat->warn($response->status(), $request->method(), $uri, $error);
} else {
$this->cat->info($response->status(), $request->method(), $uri);
}
}
}
28 changes: 28 additions & 0 deletions src/main/php/web/logging/ToConsole.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php namespace web\logging;

use util\cmd\Console;

class ToConsole extends Sink {

/**
* Writes a log entry
*
* @param web.Request $response
* @param web.Response $response
* @param ?web.Error $error Optional error
* @return void
*/
public function log($request, $response, $error) {
$query= $request->uri()->query();
Console::writeLinef(
" \e[33m[%s %d %.3fkB]\e[0m %d %s %s %s",
date('Y-m-d H:i:s'),
getmypid(),
memory_get_usage() / 1024,
$response->status(),
$request->method(),
$request->uri()->path().($query ? '?'.$query : ''),
$message
);
}
}
49 changes: 49 additions & 0 deletions src/main/php/web/logging/ToFile.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php namespace web\logging;

use io\File;
use lang\IllegalArgumentException;

/**
* Logfile sink writing to a file
*
* @test xp://web.unittest.logging.ToFileTest
*/
class ToFile extends Sink {
private $file;

/** @param string|io.File $file */
public function __construct($file) {
$this->file= $file instanceof File ? $file->getURI() : $file;
if (false === file_put_contents($this->file, '', FILE_APPEND | LOCK_EX)) {
$e= new IllegalArgumentException('Cannot write to '.$this->file);
\xp::gc(__FILE__);
throw $e;
}
}

/** @return string */
public function target() { return nameof($this).'('.$this->file.')'; }

/**
* Writes a log entry
*
* @param web.Request $response
* @param web.Response $response
* @param ?web.Error $error Optional error
* @return void
*/
public function log($request, $response, $error) {
$query= $request->uri()->query();
$line= sprintf(
"[%s %d %.3fkB] %d %s %s %s\n",
date('Y-m-d H:i:s'),
getmypid(),
memory_get_usage() / 1024,
$response->status(),
$request->method(),
$request->uri()->path().($query ? '?'.$query : ''),
$message
);
file_put_contents($this->file, $line, FILE_APPEND | LOCK_EX);
}
}
22 changes: 22 additions & 0 deletions src/main/php/web/logging/ToFunction.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php namespace web\logging;

class ToFunction extends Sink {
private $function;

/** @param callable $function */
public function __construct($function) {
$this->function= cast($function, 'function(web.Request, web.Response, ?web.Error): void');
}

/**
* Writes a log entry
*
* @param web.Request $response
* @param web.Response $response
* @param ?web.Error $error Optional error
* @return void
*/
public function log($request, $response, $error) {
$this->function->__invoke($request, $response, $error);
}
}
Loading