Added Logger interface, threaded MainLogger and updated PluginLogger

This commit is contained in:
Shoghi Cervantes 2014-05-28 23:46:56 +02:00
parent 9df56295f6
commit 7bd6f2ed91
22 changed files with 624 additions and 178 deletions

View File

@ -82,6 +82,7 @@ use pocketmine\tile\Sign;
use pocketmine\tile\Spawnable;
use pocketmine\tile\Tile;
use pocketmine\utils\Binary;
use pocketmine\utils\MainLogger;
use pocketmine\utils\TextFormat;
/**
@ -392,7 +393,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->buffer->data = [];
$this->tasks[] = $this->server->getScheduler()->scheduleRepeatingTask(new CallbackTask(array($this, "handlePacketQueues")), 1);
$this->tasks[] = $this->server->getScheduler()->scheduleRepeatingTask(new CallbackTask(array($this, "clearQueue")), 20 * 60);
console("[DEBUG] New Session started with " . $ip . ":" . $port . ". MTU " . $this->MTU . ", Client ID " . $this->clientID, true, true, 2);
$this->server->getLogger()->debug("New Session started with " . $ip . ":" . $port . ". MTU " . $this->MTU . ", Client ID " . $this->clientID);
}
/**
@ -1448,7 +1450,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->lastMeasure = microtime(true);
$this->tasks[] = $this->server->getScheduler()->scheduleRepeatingTask(new CallbackTask(array($this, "measureLag")), 50);
console("[INFO] " . TextFormat::AQUA . $this->username . TextFormat::RESET . "[/" . $this->ip . ":" . $this->port . "] logged in with entity id " . $this->id . " at (" . $this->getLevel()->getName() . ", " . round($this->x, 4) . ", " . round($this->y, 4) . ", " . round($this->z, 4) . ")");
$this->server->getLogger()->info(TextFormat::AQUA . $this->username . TextFormat::WHITE . "[/" . $this->ip . ":" . $this->port . "] logged in with entity id " . $this->id . " at (" . $this->getLevel()->getName() . ", " . round($this->x, 4) . ", " . round($this->y, 4) . ", " . round($this->z, 4) . ")");
$this->server->getPluginManager()->callEvent(new PlayerJoinEvent($this, $this->username . " joined the game"));
@ -1517,7 +1519,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->teleport($this->lastCorrect, $this->entity->yaw, $this->entity->pitch, false);
}
if($this->blocked !== true){
console("[WARNING] ".$this->username." moved too quickly!");
$this->server->getLogger()->warning($this->username." moved too quickly!");
}
}else{*/
$this->setPositionAndRotation($newPos, $packet->yaw, $packet->pitch);
@ -2163,7 +2165,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
}
break;
default:
console("[DEBUG] Unhandled " . $packet->pid() . " data packet for " . $this->username . " (" . $this->clientID . "): " . print_r($packet, true), true, true, 2);
$this->server->getLogger()->debug("Unhandled " . $packet->pid() . " data packet for " . $this->username . " (" . $this->clientID . "): " . print_r($packet, true));
break;
}
}
@ -2257,7 +2259,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
}
$this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
$this->spawned = false;
console("[INFO] " . TextFormat::AQUA . $this->username . TextFormat::RESET . "[/" . $this->ip . ":" . $this->port . "] logged out due to " . $reason);
$this->server->getLogger()->info(TextFormat::AQUA . $this->username . TextFormat::WHITE . "[/" . $this->ip . ":" . $this->port . "] logged out due to " . $reason);
$this->windows = new \SplObjectStorage();
$this->windowIndex = [];
$this->chunksLoaded = [];

View File

@ -20,18 +20,6 @@
*/
namespace {
/**
* Output text to the console, can contain Minecraft-formatted text.
*
* @param string $message
* @param bool $EOL
* @param bool $log
* @param int $level
*/
function console($message, $EOL = true, $log = true, $level = 1){
pocketmine\console($message, $EOL, $log, $level);
}
function safe_var_dump(){
static $cnt = 0;
foreach(func_get_args() as $var){
@ -78,6 +66,8 @@ namespace {
namespace pocketmine {
use pocketmine\utils\Binary;
use pocketmine\utils\LogLevel;
use pocketmine\utils\MainLogger;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use pocketmine\wizard\Installer;
@ -154,11 +144,9 @@ namespace pocketmine {
define("pocketmine\\DATA", isset($opts["data"]) ? realpath($opts["data"]) . DIRECTORY_SEPARATOR : \getcwd() . DIRECTORY_SEPARATOR);
define("pocketmine\\PLUGIN_PATH", isset($opts["plugins"]) ? realpath($opts["plugins"]) . DIRECTORY_SEPARATOR : \getcwd() . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR);
if((strpos(strtoupper(php_uname("s")), "WIN") === false or isset($opts["enable-ansi"])) and !isset($opts["disable-ansi"])){
define("pocketmine\\ANSI", true);
}else{
define("pocketmine\\ANSI", false);
}
define("pocketmine\\ANSI", ((strpos(strtoupper(php_uname("s")), "WIN") === false or isset($opts["enable-ansi"])) and !isset($opts["disable-ansi"])));
$logger = new MainLogger(\pocketmine\PATH . "server.log", \pocketmine\ANSI);
function kill($pid){
switch(Utils::getOS()){
@ -172,57 +160,6 @@ namespace pocketmine {
}
}
/**
* Output text to the console, can contain Minecraft-formatted text.
*
* @param $message
* @param bool $EOL
* @param bool $log
* @param int $level
*/
function console($message, $EOL = true, $log = true, $level = 1){
if(!defined("pocketmine\\DEBUG") or \pocketmine\DEBUG >= $level){
$message .= $EOL === true ? PHP_EOL : "";
if($message{0} !== "["){
$message = "[INFO] $message";
}
$time = (\pocketmine\ANSI === true ? TextFormat::AQUA . date("H:i:s") . TextFormat::RESET : date("H:i:s")) . " ";
$replaced = TextFormat::clean(preg_replace('/\x1b\[[0-9;]*m/', "", $time . $message));
if($log === true and (!defined("LOG") or LOG === true)){
log(date("Y-m-d") . " " . $replaced, "server", false, $level);
}
if(\pocketmine\ANSI === true){
$add = "";
if(preg_match("/^\\[([a-zA-Z0-9]*)\\]/", $message, $matches) > 0){
switch($matches[1]){
case "ERROR":
case "SEVERE":
$add .= TextFormat::RED;
break;
case "TRACE":
case "INTERNAL":
case "DEBUG":
$add .= TextFormat::GRAY;
break;
case "WARNING":
$add .= TextFormat::YELLOW;
break;
case "NOTICE":
$add .= TextFormat::AQUA;
break;
default:
$add = "";
break;
}
}
$message = TextFormat::toANSI($time . $add . $message . TextFormat::RESET);
}else{
$message = $replaced;
}
echo $message;
}
}
function getTrace($start = 1){
$e = new \Exception();
$trace = $e->getTrace();
@ -262,11 +199,12 @@ namespace pocketmine {
E_DEPRECATED => "E_DEPRECATED",
E_USER_DEPRECATED => "E_USER_DEPRECATED",
);
$type = ($errno === E_ERROR or $errno === E_WARNING or $errno === E_USER_ERROR or $errno === E_USER_WARNING) ? "ERROR" : "NOTICE";
$type = ($errno === E_ERROR or $errno === E_WARNING or $errno === E_USER_ERROR or $errno === E_USER_WARNING) ? LogLevel::ERROR : LogLevel::NOTICE;
$errno = isset($errorConversion[$errno]) ? $errorConversion[$errno] : $errno;
console("[$type] A $errno error happened: \"$errstr\" in \"$errfile\" at line $errline", true, true, 0);
$logger = MainLogger::getLogger();
$logger->log($type, "A $errno error happened: \"$errstr\" in \"$errfile\" at line $errline");
foreach(getTrace() as $i => $line){
console("[TRACE] $line");
$logger->debug($line);
}
return true;
@ -296,22 +234,22 @@ namespace pocketmine {
$errors = 0;
if(version_compare("5.4.0", PHP_VERSION) > 0){
console("[ERROR] Use PHP >= 5.4.0", true, true, 0);
$logger->critical("Use PHP >= 5.4.0");
++$errors;
}
if(php_sapi_name() !== "cli"){
console("[ERROR] You must run PocketMine-MP using the CLI.", true, true, 0);
$logger->critical("You must run PocketMine-MP using the CLI.");
++$errors;
}
if(!extension_loaded("sockets")){
console("[ERROR] Unable to find the Socket extension.", true, true, 0);
$logger->critical("Unable to find the Socket extension.");
++$errors;
}
if(!extension_loaded("pthreads")){
console("[ERROR] Unable to find the pthreads extension.", true, true, 0);
$logger->critical("Unable to find the pthreads extension.");
++$errors;
}else{
$pthreads_version = phpversion("pthreads");
@ -319,52 +257,54 @@ namespace pocketmine {
$pthreads_version = "0.$pthreads_version";
}
if(version_compare($pthreads_version, "2.0.4") < 0){
console("[ERROR] pthreads >= 2.0.4 is required, while you have $pthreads_version.", true, true, 0);
$logger->critical("pthreads >= 2.0.4 is required, while you have $pthreads_version.");
++$errors;
}
}
if(!extension_loaded("uopz")){
//console("[NOTICE] Couldn't find the uopz extension. Some functions may be limited", true, true, 0);
//$logger->notice("Couldn't find the uopz extension. Some functions may be limited");
}
if(extension_loaded("pocketmine")){
if(version_compare(phpversion("pocketmine"), "0.0.1") < 0){
console("[ERROR] You have the native PocketMine extension, but your version is lower than 0.0.1.", true, true, 0);
$logger->critical("You have the native PocketMine extension, but your version is lower than 0.0.1.");
++$errors;
}elseif(version_compare(phpversion("pocketmine"), "0.0.4") > 0){
console("[ERROR] You have the native PocketMine extension, but your version is higher than 0.0.4.", true, true, 0);
$logger->critical("You have the native PocketMine extension, but your version is higher than 0.0.4.");
++$errors;
}
}
if(!extension_loaded("Weakref") and !extension_loaded("weakref")){
console("[ERROR] Unable to find the Weakref extension.", true, true, 0);
$logger->critical("Unable to find the Weakref extension.");
++$errors;
}
if(!extension_loaded("curl")){
console("[ERROR] Unable to find the cURL extension.", true, true, 0);
$logger->critical("Unable to find the cURL extension.");
++$errors;
}
if(!extension_loaded("sqlite3")){
console("[ERROR] Unable to find the SQLite3 extension.", true, true, 0);
$logger->critical("Unable to find the SQLite3 extension.");
++$errors;
}
if(!extension_loaded("yaml")){
console("[ERROR] Unable to find the YAML extension.", true, true, 0);
$logger->critical("Unable to find the YAML extension.");
++$errors;
}
if(!extension_loaded("zlib")){
console("[ERROR] Unable to find the Zlib extension.", true, true, 0);
$logger->critical("Unable to find the Zlib extension.");
++$errors;
}
if($errors > 0){
console("[ERROR] Please use the installer provided on the homepage, or recompile PHP again.", true, true, 0);
$logger->critical("Please use the installer provided on the homepage, or recompile PHP again.");
$logger->shutdown();
$logger->join();
exit(1); //Exit with error
}
@ -383,11 +323,13 @@ namespace pocketmine {
}
if(substr(__FILE__, 0, 7) !== "phar://"){
console("[WARNING] Non-packaged PocketMine-MP installation detected, do not use on production.");
$logger->warning("Non-packaged PocketMine-MP installation detected, do not use on production.");
}
$server = new Server($autoloader, \pocketmine\PATH, \pocketmine\DATA, \pocketmine\PLUGIN_PATH);
$server = new Server($autoloader, $logger, \pocketmine\PATH, \pocketmine\DATA, \pocketmine\PLUGIN_PATH);
$server->start();
$logger->shutdown();
$logger->join();
kill(getmypid());
exit(0);

View File

@ -84,6 +84,7 @@ use pocketmine\tile\Sign;
use pocketmine\tile\Tile;
use pocketmine\utils\Binary;
use pocketmine\utils\Config;
use pocketmine\utils\Logger;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use pocketmine\utils\VersionString;
@ -122,6 +123,9 @@ class Server{
/** @var TickScheduler */
private $tickScheduler = null;
/** @var \pocketmine\utils\Logger */
private $logger;
/** @var CommandReader */
private $console = null;
@ -444,6 +448,13 @@ class Server{
return $this->autoloader;
}
/**
* @return Logger
*/
public function getLogger(){
return $this->logger;
}
/**
* @return EntityMetadataStore
*/
@ -602,7 +613,7 @@ class Server{
$nbt["SpawnX"] = (int) $data->get("spawn")["x"];
$nbt["SpawnY"] = (int) $data->get("spawn")["y"];
$nbt["SpawnZ"] = (int) $data->get("spawn")["z"];
console("[NOTICE] Old Player data found for \"" . $name . "\", upgrading profile");
$this->logger->notice("Old Player data found for \"" . $name . "\", upgrading profile");
foreach($data->get("inventory") as $slot => $item){
if(count($item) === 3){
$nbt->Inventory[$slot + 9] = new Compound(false, array(
@ -641,7 +652,7 @@ class Server{
}
unlink($path . "$name.yml");
}else{
console("[NOTICE] Player data not found for \"" . $name . "\", creating new profile");
$this->logger->notice("Player data not found for \"" . $name . "\", creating new profile");
}
$this->saveOfflinePlayerData($name, $nbt);
@ -809,13 +820,13 @@ class Server{
if($this->isLevelLoaded($name)){
return true;
}elseif(!$this->isLevelGenerated($name)){
console("[NOTICE] Level \"" . $name . "\" not found");
$this->logger->notice("Level \"" . $name . "\" not found");
return false;
}
$path = $this->getDataPath() . "worlds/" . $name . "/";
console("[INFO] Preparing level \"" . $name . "\"");
$this->logger->info("Preparing level \"" . $name . "\"");
$level = new LevelFormat($path . "level.pmf");
if(!$level->isLoaded){
console("[ERROR] Could not load level \"" . $name . "\"");
@ -1175,14 +1186,16 @@ class Server{
/**
* @param \SplClassLoader $autoloader
* @param Logger $logger
* @param string $filePath
* @param string $dataPath
* @param string $pluginPath
*/
public function __construct(\SplClassLoader $autoloader, $filePath, $dataPath, $pluginPath){
public function __construct(\SplClassLoader $autoloader, Logger $logger, $filePath, $dataPath, $pluginPath){
self::$instance = $this;
$this->autoloader = $autoloader;
$this->logger = $logger;
$this->filePath = $filePath;
$this->dataPath = $dataPath;
$this->pluginPath = $pluginPath;
@ -1211,9 +1224,9 @@ class Server{
$this->console = new CommandReader();
$version = new VersionString($this->getPocketMineVersion());
console("[INFO] Starting Minecraft: PE server version " . TextFormat::AQUA . $this->getVersion());
$this->logger->info("Starting Minecraft: PE server version " . TextFormat::AQUA . $this->getVersion());
console("[INFO] Loading properties...");
$this->logger->info("Loading properties...");
$this->properties = new Config($this->dataPath . "server.properties", Config::PROPERTIES, array(
"motd" => "Minecraft: PE Server",
"server-port" => 19132,
@ -1250,7 +1263,7 @@ class Server{
$value = array("M" => 1, "G" => 1024);
$real = ((int) substr($memory, 0, -1)) * $value[substr($memory, -1)];
if($real < 128){
console("[WARNING] PocketMine-MP may not work right with less than 128MB of RAM", true, true, 0);
$this->logger->warning("PocketMine-MP may not work right with less than 128MB of RAM", true, true, 0);
}
@ini_set("memory_limit", $memory);
}else{
@ -1265,20 +1278,20 @@ class Server{
define("ADVANCED_CACHE", $this->getConfigBoolean("enable-advanced-cache", false));
define("MAX_CHUNK_RATE", 20 / $this->getConfigInt("max-chunks-per-second", 7)); //Default rate ~448 kB/s
if(ADVANCED_CACHE == true){
console("[INFO] Advanced cache enabled");
$this->logger->info("Advanced cache enabled");
}
if(defined("pocketmine\\DEBUG") and \pocketmine\DEBUG >= 0 and function_exists("cli_set_process_title")){
@cli_set_process_title("PocketMine-MP " . $this->getPocketMineVersion());
}
console("[INFO] Starting Minecraft PE server on " . ($this->getIp() === "" ? "*" : $this->getIp()) . ":" . $this->getPort());
$this->logger->info("Starting Minecraft PE server on " . ($this->getIp() === "" ? "*" : $this->getIp()) . ":" . $this->getPort());
define("BOOTUP_RANDOM", Utils::getRandomBytes(16));
$this->serverID = Binary::readLong(substr(Utils::getUniqueID(true, $this->getIp() . $this->getPort()), 0, 8));
$this->interface = new ThreadedHandler("255.255.255.255", $this->getPort(), $this->getIp() === "" ? "0.0.0.0" : $this->getIp());
console("[INFO] This server is running PocketMine-MP version " . ($version->isDev() ? TextFormat::YELLOW : "") . $this->getPocketMineVersion() . TextFormat::RESET . " \"" . $this->getCodename() . "\" (API " . $this->getApiVersion() . ")", true, true, 0);
console("[INFO] PocketMine-MP is distributed under the LGPL License", true, true, 0);
$this->logger->info("This server is running PocketMine-MP version " . ($version->isDev() ? TextFormat::YELLOW : "") . $this->getPocketMineVersion() . TextFormat::RESET . " \"" . $this->getCodename() . "\" (API " . $this->getApiVersion() . ")", true, true, 0);
$this->logger->info("PocketMine-MP is distributed under the LGPL License", true, true, 0);
$this->consoleSender = new ConsoleCommandSender();
$this->commandMap = new SimpleCommandMap($this);
@ -1331,8 +1344,8 @@ class Server{
/*
//TODO
if($this->getProperty("last-update") === false or ($this->getProperty("last-update") + 3600) < time()){
console("[INFO] Checking for new server version");
console("[INFO] Last check: " . TextFormat::AQUA . date("Y-m-d H:i:s", $this->getProperty("last-update")) . "\x1b[0m");
$this->logger->info("Checking for new server version");
$this->logger->info("Last check: " . TextFormat::AQUA . date("Y-m-d H:i:s", $this->getProperty("last-update")));
if($this->server->version->isDev()){
$info = json_decode(Utils::getURL("https://api.github.com/repos/PocketMine/PocketMine-MP/commits"), true);
if($info === false or !isset($info[0])){
@ -1341,13 +1354,13 @@ class Server{
$last = new \DateTime($info[0]["commit"]["committer"]["date"]);
$last = $last->getTimestamp();
if($last >= $this->getProperty("last-update") and $this->getProperty("last-update") !== false and \pocketmine\GIT_COMMIT != $info[0]["sha"]){
console("[NOTICE] " . TextFormat::YELLOW . "A new DEVELOPMENT version of PocketMine-MP has been released!");
console("[NOTICE] " . TextFormat::YELLOW . "Commit \"" . $info[0]["commit"]["message"] . "\" [" . substr($info[0]["sha"], 0, 10) . "] by " . $info[0]["commit"]["committer"]["name"]);
console("[NOTICE] " . TextFormat::YELLOW . "Get it at PocketMine.net or at https://github.com/PocketMine/PocketMine-MP/archive/" . $info[0]["sha"] . ".zip");
console("[NOTICE] This message will disappear after issuing the command \"/update-done\"");
$this->logger->notice("" . TextFormat::YELLOW . "A new DEVELOPMENT version of PocketMine-MP has been released!");
$this->logger->notice("" . TextFormat::YELLOW . "Commit \"" . $info[0]["commit"]["message"] . "\" [" . substr($info[0]["sha"], 0, 10) . "] by " . $info[0]["commit"]["committer"]["name"]);
$this->logger->notice("" . TextFormat::YELLOW . "Get it at PocketMine.net or at https://github.com/PocketMine/PocketMine-MP/archive/" . $info[0]["sha"] . ".zip");
$this->logger->notice("This message will disappear after issuing the command \"/update-done\"");
}else{
$this->setProperty("last-update", time());
console("[INFO] " . TextFormat::AQUA . "This is the latest DEVELOPMENT version");
$this->logger->info("" . TextFormat::AQUA . "This is the latest DEVELOPMENT version");
}
}
}else{
@ -1360,13 +1373,13 @@ class Server{
$update = new VersionString($info[0]["name"]);
$updateN = $update->getNumber();
if($updateN > $newestN){
console("[NOTICE] " . TextFormat::GREEN . "A new STABLE version of PocketMine-MP has been released!");
console("[NOTICE] " . TextFormat::GREEN . "Version \"" . $info[0]["name"] . "\" #" . $updateN);
console("[NOTICE] Get it at PocketMine.net or at " . $info[0]["zipball_url"]);
console("[NOTICE] This message will disappear as soon as you update");
$this->logger->notice("" . TextFormat::GREEN . "A new STABLE version of PocketMine-MP has been released!");
$this->logger->notice("" . TextFormat::GREEN . "Version \"" . $info[0]["name"] . "\" #" . $updateN);
$this->logger->notice("Get it at PocketMine.net or at " . $info[0]["zipball_url"]);
$this->logger->notice("This message will disappear as soon as you update");
}else{
$this->setProperty("last-update", time());
console("[INFO] " . TextFormat::AQUA . "This is the latest STABLE version");
$this->logger->info("" . TextFormat::AQUA . "This is the latest STABLE version");
}
}
}
@ -1475,7 +1488,7 @@ class Server{
}
public function reload(){
console("[INFO] Saving levels...");
$this->logger->info("Saving levels...");
foreach($this->levels as $level){
$level->save();
@ -1485,7 +1498,7 @@ class Server{
$this->pluginManager->clearPlugins();
$this->commandMap->clearCommands();
console("[INFO] Reloading properties...");
$this->logger->info("Reloading properties...");
$this->properties->reload();
$this->maxPlayers = $this->getConfigInt("max-players", 20);
@ -1493,7 +1506,7 @@ class Server{
$value = array("M" => 1, "G" => 1024);
$real = ((int) substr($memory, 0, -1)) * $value[substr($memory, -1)];
if($real < 128){
console("[WARNING] PocketMine-MP may not work right with less than 128MB of RAM", true, true, 0);
$this->logger->warning("PocketMine-MP may not work right with less than 128MB of RAM", true, true, 0);
}
@ini_set("memory_limit", $memory);
}else{
@ -1530,7 +1543,7 @@ class Server{
}
if($this->getConfigBoolean("upnp-forwarding", false) === true){
console("[INFO] [UPnP] Removing port forward...");
$this->logger->info("[UPnP] Removing port forward...");
UPnP::RemovePortForward($this->getPort());
}
@ -1571,7 +1584,7 @@ class Server{
if($this->getConfigBoolean("upnp-forwarding", false) == true){
console("[INFO] [UPnP] Trying to port forward...");
$this->logger->info("[UPnP] Trying to port forward...");
UPnP::PortForward($this->getPort());
}
@ -1585,9 +1598,9 @@ class Server{
pcntl_signal(SIGHUP, array($this, "shutdown"));
}
console("[INFO] Default game type: " . self::getGamemodeString($this->getGamemode())); //TODO: string name
$this->logger->info("Default game type: " . self::getGamemodeString($this->getGamemode())); //TODO: string name
console('[INFO] Done (' . round(microtime(true) - \pocketmine\START_TIME, 3) . 's)! For help, type "help" or "?"');
$this->logger->info("Done (" . round(microtime(true) - \pocketmine\START_TIME, 3) . 's)! For help, type "help" or "?"');
if(Utils::getOS() === "win"){ //Workaround less usleep() waste
$this->tickProcessorWindows();
}else{
@ -1619,7 +1632,7 @@ class Server{
public function checkTicks(){
if($this->getTicksPerSecond() < 12){
console("[WARNING] Can't keep up! Is the server overloaded?");
$this->logger->warning("Can't keep up! Is the server overloaded?");
}
}
@ -1639,7 +1652,7 @@ class Server{
return;
}
ini_set("memory_limit", "-1"); //Fix error dump not dumped on memory problems
console("[SEVERE] An unrecoverable has occurred and the server has crashed. Creating an error dump");
$this->logger->emergency("An unrecoverable has occurred and the server has crashed. Creating an error dump");
$dump = "```\r\n# PocketMine-MP Error Dump " . date("D M j H:i:s T Y") . "\r\n";
$er = error_get_last();
$errorConversion = array(
@ -1715,7 +1728,7 @@ class Server{
$dump .= "\r\n```";
$name = "Error_Dump_" . date("D_M_j-H.i.s-T_Y");
log($dump, $name, true, 0, true);
console("[SEVERE] Please submit the \"{$name}.log\" file to the Bug Reporting page. Give as much info as you can.", true, true, 0);
$this->logger->emergency("Please submit the \"{$name}.log\" file to the Bug Reporting page. Give as much info as you can.", true, true, 0);
}
private function tickProcessor(){
@ -1764,7 +1777,7 @@ class Server{
break;
case RakNetInfo::OPEN_CONNECTION_REQUEST_1:
if($packet->structure !== RakNetInfo::STRUCTURE){
console("[DEBUG] Incorrect structure #" . $packet->structure . " from " . $packet->ip . ":" . $packet->port, true, true, 2);
$this->logger->debug("Incorrect structure #" . $packet->structure . " from " . $packet->ip . ":" . $packet->port);
$pk = new RakNetPacket(RakNetInfo::INCOMPATIBLE_PROTOCOL_VERSION);
$pk->serverID = $this->serverID;
$pk->ip = $packet->ip;

View File

@ -25,6 +25,7 @@ use pocketmine\permission\PermissibleBase;
use pocketmine\permission\PermissionAttachment;
use pocketmine\plugin\Plugin;
use pocketmine\Server;
use pocketmine\utils\MainLogger;
class ConsoleCommandSender implements CommandSender{
@ -102,8 +103,7 @@ class ConsoleCommandSender implements CommandSender{
*/
public function sendMessage($message){
foreach(explode("\n", trim($message)) as $line){
$line = trim($line);
console($line);
MainLogger::getLogger()->info($line);
}
}

View File

@ -161,7 +161,7 @@ class Level{
if($this === $this->server->getDefaultLevel() and $force !== true){
return false;
}
console("[INFO] Unloading level \"" . $this->getName() . "\"");
$this->server->getLogger()->info("Unloading level \"" . $this->getName() . "\"");
$this->nextSave = PHP_INT_MAX;
$this->save();
$defaultLevel = $this->server->getDefaultLevel();

View File

@ -25,6 +25,7 @@ use pocketmine\level\format\pmf\LevelFormat;
use pocketmine\level\format\PocketChunkParser;
use pocketmine\nbt\NBT;
use pocketmine\utils\Config;
use pocketmine\utils\MainLogger;
class LevelImport{
private $path;
@ -36,7 +37,7 @@ class LevelImport{
public function import(){
if(file_exists($this->path . "tileEntities.dat")){ //OldPM
$level = unserialize(file_get_contents($this->path . "level.dat"));
console("[INFO] Importing OldPM level \"" . $level["LevelName"] . "\" to PMF format");
MainLogger::getLogger()->info("Importing OldPM level \"" . $level["LevelName"] . "\" to PMF format");
$entities = new Config($this->path . "entities.yml", Config::YAML, unserialize(file_get_contents($this->path . "entities.dat")));
$entities->save();
$tiles = new Config($this->path . "tiles.yml", Config::YAML, unserialize(file_get_contents($this->path . "tileEntities.dat")));
@ -48,7 +49,7 @@ class LevelImport{
if($level["LevelName"] == ""){
$level["LevelName"] = "world" . time();
}
console("[INFO] Importing Pocket level \"" . $level->LevelName . "\" to PMF format");
MainLogger::getLogger()->info("Importing Pocket level \"" . $level->LevelName . "\" to PMF format");
unset($level->Player);
$nbt->read(substr(file_get_contents($this->path . "entities.dat"), 12));
$entities = $nbt->getData();
@ -111,7 +112,7 @@ class LevelImport{
$pmf->setPopulated($X, $Z);
$pmf->saveChunk($X, $Z);
}
console("[NOTICE] Importing level " . ceil(($Z + 1) / 0.16) . "%");
MainLogger::getLogger()->notice("Importing level " . ceil(($Z + 1) / 0.16) . "%");
}
$chunks->map = null;
$chunks = null;

View File

@ -41,7 +41,6 @@ class PocketChunkParser{
private function loadLocationTable(){
$this->location = [];
console("[DEBUG] Loading Chunk Location table...", true, true, 2);
for($offset = 0; $offset < 0x1000; $offset += 4){
$data = Binary::readLInt(substr($this->raw, $offset, 4));
$sectors = $data & 0xff;
@ -136,7 +135,6 @@ class PocketChunkParser{
return false;
}
$this->loadLocationTable();
console("[DEBUG] Loading chunks...", true, true, 2);
for($x = 0; $x < 16; ++$x){
$this->map[$x] = [];
for($z = 0; $z < 16; ++$z){
@ -144,13 +142,11 @@ class PocketChunkParser{
}
}
$this->raw = "";
console("[DEBUG] Chunks loaded!", true, true, 2);
return true;
}
public function saveMap($final = false){
console("[DEBUG] Saving chunks...", true, true, 2);
$fp = fopen($this->file, "r+b");
flock($fp, LOCK_EX);
@ -165,7 +161,6 @@ class PocketChunkParser{
$original = filesize($this->file);
file_put_contents($this->file . ".gz", gzdeflate(gzdeflate(file_get_contents($this->file), 9), 9)); //Double compression for flat maps
$compressed = filesize($this->file . ".gz");
console("[DEBUG] Saved chunks.dat.gz with " . round(($compressed / $original) * 100, 2) . "% (" . round($compressed / 1024, 2) . "KB) of the original size", true, true, 2);
if($final === true){
@unlink($this->file);
}

View File

@ -26,6 +26,7 @@ use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\Compound;
use pocketmine\nbt\tag\Enum;
use pocketmine\utils\Binary;
use pocketmine\utils\MainLogger;
class LevelFormat extends PMF{
const VERSION = 2;
@ -121,7 +122,7 @@ class LevelFormat extends PMF{
$this->seek(5);
$this->levelData["version"] = ord($this->read(1));
if($this->levelData["version"] > self::VERSION){
console("[ERROR] New unsupported PMF Level format version #" . $this->levelData["version"] . ", current version is #" . self::VERSION);
MainLogger::getLogger()->error("New unsupported PMF Level format version #" . $this->levelData["version"] . ", current version is #" . self::VERSION);
return false;
}
@ -161,7 +162,7 @@ class LevelFormat extends PMF{
}
private function upgrade_From0_To1(){
console("[NOTICE] Old PMF Level format version #0 detected, upgrading to version #1");
MainLogger::getLogger()->notice("Old PMF Level format version #0 detected, upgrading to version #1");
for($index = 0; $index < 256; ++$index){
$X = $index & 0x0F;
$Z = $index >> 4;
@ -186,7 +187,7 @@ class LevelFormat extends PMF{
}
private function upgrade_From1_To2(){
console("[NOTICE] Old PMF Level format version #1 detected, upgrading to version #2");
MainLogger::getLogger()->notice("Old PMF Level format version #1 detected, upgrading to version #2");
$nbt = new Compound("", array(
new Enum("Entities", []),
new Enum("TileEntities", [])
@ -308,7 +309,7 @@ class LevelFormat extends PMF{
if(($this->chunkInfo[$index][0] & (1 << $Y)) !== 0){
// 4096 + 2048 + 2048, Block Data, Meta, Light
if(strlen($this->chunks[$index][$Y] = substr($chunk, $offset, 8192)) < 8192){
console("[NOTICE] Empty corrupt chunk detected [$X,$Z,:$Y], recovering contents", true, true, 2);
MainLogger::getLogger()->notice("Empty corrupt chunk detected [$X,$Z,:$Y], recovering contents");
$this->fillMiniChunk($X, $Z, $Y);
}
$offset += 8192;

View File

@ -25,6 +25,8 @@
namespace pocketmine\level\format\pmf;
use pocketmine\utils\MainLogger;
class PMF{
const VERSION = 0x01;
@ -78,7 +80,7 @@ class PMF{
$this->type = ord($this->read(1));
break;
default:
console("[ERROR] Tried loading non-supported PMF version " . $this->version . " on file " . $this->file);
MainLogger::getLogger()->alert("Tried loading non-supported PMF version " . $this->version . " on file " . $this->file);
return false;
}

View File

@ -27,6 +27,7 @@ namespace pocketmine\network;
use pocketmine\network\query\QueryPacket;
use pocketmine\network\raknet\Info;
use pocketmine\network\raknet\Packet as RakNetPacket;
use pocketmine\utils\MainLogger;
class ThreadedHandler extends \Thread{
protected $bandwidthUp;
@ -124,8 +125,8 @@ class ThreadedHandler extends \Thread{
@socket_set_option($this->socket, SOL_SOCKET, SO_SNDBUF, 1024 * 1024 * 2); //2MB
@socket_set_option($this->socket, SOL_SOCKET, SO_RCVBUF, 1024 * 1024); //1MB
}else{
console("[SEVERE] **** FAILED TO BIND TO " . $this->serverip . ":" . $this->port . "!", true, true, 0);
console("[SEVERE] Perhaps a server is already running on that port?", true, true, 0);
MainLogger::getLogger()->critical("**** FAILED TO BIND TO " . $this->serverip . ":" . $this->port . "!", true, true, 0);
MainLogger::getLogger()->critical("Perhaps a server is already running on that port?", true, true, 0);
exit(1);
}
socket_set_nonblock($this->socket);

View File

@ -33,11 +33,11 @@ class QueryHandler{
private $socket, $server, $lastToken, $token, $longData, $timeout;
public function __construct(){
console("[INFO] Starting GS4 status listener");
$this->server = Server::getInstance();
$this->server->getLogger()->info("Starting GS4 status listener");
$addr = ($ip = $this->server->getIp()) != "" ? $ip : "0.0.0.0";
$port = $this->server->getPort();
console("[INFO] Setting query port to $port");
$this->server->getLogger()->info("Setting query port to $port");
/*
The Query protocol is built on top of the existing Minecraft PE UDP network stack.
Because the 0xFE packet does not exist in the MCPE protocol,
@ -50,7 +50,7 @@ class QueryHandler{
$this->regenerateToken();
$this->lastToken = $this->token;
$this->regenerateInfo();
console("[INFO] Query running on $addr:$port");
$this->server->getLogger()->info("Query running on $addr:$port");
}
public function regenerateInfo(){

View File

@ -28,6 +28,7 @@ namespace pocketmine\network\rcon;
use pocketmine\command\RemoteConsoleCommandSender;
use pocketmine\scheduler\CallbackTask;
use pocketmine\Server;
use pocketmine\utils\MainLogger;
use pocketmine\utils\TextFormat;
@ -43,9 +44,9 @@ class RCON{
public function __construct($password, $port = 19132, $interface = "0.0.0.0", $threads = 1, $clientsPerThread = 50){
$this->workers = [];
$this->password = (string) $password;
console("[INFO] Starting remote control listener");
MainLogger::getLogger()->info("Starting remote control listener");
if($this->password === ""){
console("[ERROR] RCON can't be started: Empty password");
MainLogger::getLogger()->critical("RCON can't be started: Empty password");
return;
}
@ -53,7 +54,7 @@ class RCON{
$this->clientsPerThread = (int) max(1, $clientsPerThread);
$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if($this->socket === false or !socket_bind($this->socket, $interface, (int) $port) or !socket_listen($this->socket)){
console("[ERROR] RCON can't be started: " . socket_strerror(socket_last_error()));
MainLogger::getLogger()->critical("RCON can't be started: " . socket_strerror(socket_last_error()));
return;
}
@ -63,7 +64,7 @@ class RCON{
$this->workers[$n] = new RCONInstance($this->socket, $this->password, $this->clientsPerThread);
}
@socket_getsockname($this->socket, $addr, $port);
console("[INFO] RCON running on $addr:$port");
MainLogger::getLogger()->info("RCON running on $addr:$port");
Server::getInstance()->getScheduler()->scheduleRepeatingTask(new CallbackTask(array($this, "check")), 3);
}
@ -83,7 +84,7 @@ class RCON{
$this->workers[$n] = new RCONInstance($this->socket, $this->password, $this->clientsPerThread);
}elseif($this->workers[$n]->isWaiting()){
if($this->workers[$n]->response !== ""){
console($this->workers[$n]->response);
MainLogger::getLogger()->info($this->workers[$n]->response);
$this->workers[$n]->notify();
}else{
Server::getInstance()->dispatchCommand($response = new RemoteConsoleCommandSender(), $this->workers[$n]->cmd);

View File

@ -22,6 +22,7 @@
namespace pocketmine\permission;
use pocketmine\Server;
use pocketmine\utils\MainLogger;
class BanList{
@ -141,7 +142,7 @@ class BanList{
}
fclose($fp);
}else{
console("[ERROR] Could not load ban list");
MainLogger::getLogger()->error("Could not load ban list");
}
}
@ -159,7 +160,7 @@ class BanList{
}
fclose($fp);
}else{
console("[ERROR] Could not save ban list");
MainLogger::getLogger()->error("Could not save ban list");
}
}

View File

@ -24,6 +24,7 @@ namespace pocketmine\plugin;
use pocketmine\event\plugin\PluginDisableEvent;
use pocketmine\event\plugin\PluginEnableEvent;
use pocketmine\Server;
use pocketmine\utils\MainLogger;
/**
* Handles different types of plugins
@ -51,7 +52,7 @@ class PharPluginLoader implements PluginLoader{
*/
public function loadPlugin($file){
if(\Phar::isValidPharFilename($file) and ($description = $this->getPluginDescription($file)) instanceof PluginDescription){
console("[INFO] Loading " . $description->getFullName());
MainLogger::getLogger()->info("Loading " . $description->getFullName());
$dataFolder = dirname($file) . DIRECTORY_SEPARATOR . $description->getName();
if(file_exists($dataFolder) and !is_dir($dataFolder)){
throw new \Exception("Projected dataFolder '" . $dataFolder . "' for " . $description->getName() . " exists and is not a directory");
@ -121,7 +122,7 @@ class PharPluginLoader implements PluginLoader{
*/
public function enablePlugin(Plugin $plugin){
if($plugin instanceof PluginBase and !$plugin->isEnabled()){
console("[INFO] Enabling " . $plugin->getDescription()->getFullName());
MainLogger::getLogger()->info("Enabling " . $plugin->getDescription()->getFullName());
$plugin->setEnabled(true);
@ -134,7 +135,7 @@ class PharPluginLoader implements PluginLoader{
*/
public function disablePlugin(Plugin $plugin){
if($plugin instanceof PluginBase and $plugin->isEnabled()){
console("[INFO] Disabling " . $plugin->getDescription()->getFullName());
MainLogger::getLogger()->info("Disabling " . $plugin->getDescription()->getFullName());
Server::getInstance()->getPluginManager()->callEvent(new PluginDisableEvent($plugin));

View File

@ -241,7 +241,7 @@ abstract class PluginBase implements Plugin{
public function saveConfig(){
if($this->getConfig()->save() === false){
console("[SEVERE] Could not save config to " . $this->configFile);
$this->getLogger()->critical("Could not save config to " . $this->configFile);
}
}

View File

@ -21,7 +21,14 @@
namespace pocketmine\plugin;
class PluginLogger{
use pocketmine\level\Level;
use pocketmine\Server;
use pocketmine\utils\Logger;
use pocketmine\utils\LogLevel;
use pocketmine\utils\MainLogger;
use pocketmine\utils\TextFormat;
class PluginLogger implements Logger{
private $pluginName;
@ -33,12 +40,39 @@ class PluginLogger{
$this->pluginName = $prefix != null ? "[$prefix] " : "[" . $context->getDescription()->getName() . "] ";
}
/**
* Logs a message to the console
*
* @param string $message
*/
public function log($message){
console($this->pluginName . $message);
public function emergency($message){
$this->log(LogLevel::EMERGENCY, $message);
}
public function alert($message){
$this->log(LogLevel::ALERT, $message);
}
public function critical($message){
$this->log(LogLevel::CRITICAL, $message);
}
public function error($message){
$this->log(LogLevel::ERROR, $message);
}
public function warning($message){
$this->log(LogLevel::WARNING, $message);
}
public function notice($message){
$this->log(LogLevel::NOTICE, $message);
}
public function info($message){
$this->log(LogLevel::INFO, $message);
}
public function debug($message){
$this->log(LogLevel::DEBUG, $message);
}
public function log($level, $message){
MainLogger::getLogger()->log($level, $this->pluginName . $message);
}
}

View File

@ -189,14 +189,14 @@ class PluginManager{
if($description instanceof PluginDescription){
$name = $description->getName();
if(stripos($name, "pocketmine") !== false or stripos($name, "minecraft") !== false or stripos($name, "mojang") !== false){
console("[ERROR] Could not load plugin '" . $name . "': restricted name");
$this->server->getLogger()->error("Could not load plugin '" . $name . "': restricted name");
continue;
}elseif(strpos($name, " ") !== false){
console("[WARNING] Plugin '" . $name . "' uses spaces in its name, this is discouraged");
$this->server->getLogger()->warning("Plugin '" . $name . "' uses spaces in its name, this is discouraged");
}
if(isset($plugins[$name]) or $this->getPlugin($name) instanceof Plugin){
console("[ERROR] Could not load duplicate plugin '" . $name . "': plugin exists");
$this->server->getLogger()->error("Could not load duplicate plugin '" . $name . "': plugin exists");
continue;
}
@ -220,7 +220,7 @@ class PluginManager{
}
if($compatible === false){
console("[ERROR] Could not load plugin '" . $name . "': API version not compatible");
$this->server->getLogger()->error("Could not load plugin '" . $name . "': API version not compatible");
continue;
}
@ -249,7 +249,7 @@ class PluginManager{
if(isset($loadedPlugins[$dependency]) or $this->getPlugin($dependency) instanceof Plugin){
unset($dependencies[$name][$key]);
}elseif(!isset($plugins[$dependency])){
console("[SEVERE] Could not load plugin '" . $name . "': Unknown dependency");
$this->server->getLogger()->critical("Could not load plugin '" . $name . "': Unknown dependency");
break;
}
}
@ -277,7 +277,7 @@ class PluginManager{
if($plugin = $this->loadPlugin($file) and $plugin instanceof Plugin){
$loadedPlugins[$name] = $plugin;
}else{
console("[SEVERE] Could not load plugin '" . $name . "'");
$this->server->getLogger()->critical("Could not load plugin '" . $name . "'");
}
}
}
@ -291,7 +291,7 @@ class PluginManager{
if($plugin = $this->loadPlugin($file) and $plugin instanceof Plugin){
$loadedPlugins[$name] = $plugin;
}else{
console("[SEVERE] Could not load plugin '" . $name . "'");
$this->server->getLogger()->critical("Could not load plugin '" . $name . "'");
}
}
}
@ -299,7 +299,7 @@ class PluginManager{
//No plugins loaded :(
if($missingDependency === true){
foreach($plugins as $name => $file){
console("[SEVERE] Could not load plugin '" . $name . "': circular dependency detected");
$this->server->getLogger()->critical("Could not load plugin '" . $name . "': circular dependency detected");
}
$plugins = [];
}
@ -542,7 +542,7 @@ class PluginManager{
foreach($plugin->getDescription()->getCommands() as $key => $data){
if(strpos($key, ":") !== false){
console("[SEVERE] Could not load command " . $key . " for plugin " . $plugin->getDescription()->getName());
$this->server->getLogger()->critical("Could not load command " . $key . " for plugin " . $plugin->getDescription()->getName());
continue;
}
if(is_array($data)){
@ -559,7 +559,7 @@ class PluginManager{
$aliasList = [];
foreach($data["aliases"] as $alias){
if(strpos($alias, ":") !== false){
console("[SEVERE] Could not load alias " . $alias . " for plugin " . $plugin->getDescription()->getName());
$this->server->getLogger()->critical("Could not load alias " . $alias . " for plugin " . $plugin->getDescription()->getName());
continue;
}
$aliasList[] = $alias;

View File

@ -410,7 +410,7 @@ class Config{
break;
}
if(isset($this->config[$k])){
console("[NOTICE] [Config] Repeated property " . $k . " on file " . $this->file, true, true, 2);
MainLogger::getLogger()->debug("[Config] Repeated property " . $k . " on file " . $this->file);
}
$this->config[$k] = $v;
}

View File

@ -0,0 +1,33 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\utils;
interface LogLevel{
const EMERGENCY = "emergency";
const ALERT = "alert";
const CRITICAL = "critical";
const ERROR = "error";
const WARNING = "warning";
const NOTICE = "notice";
const INFO = "info";
const DEBUG = "debug";
}

View File

@ -0,0 +1,93 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\utils;
interface Logger{
/**
* System is unusable
*
* @param string $message
*/
public function emergency($message);
/**
* Action must me taken immediately
*
* @param string $message
*/
public function alert($message);
/**
* Critical conditions
*
* @param string $message
*/
public function critical($message);
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message
*/
public function error($message);
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string $message
*/
public function warning($message);
/**
* Normal but significant events.
*
* @param string $message
*/
public function notice($message);
/**
* Inersting events.
*
* @param string $message
*/
public function info($message);
/**
* Detailed debug information.
*
* @param string $message
*/
public function debug($message);
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
*/
public function log($level, $message);
}

View File

@ -0,0 +1,163 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\utils;
class MainLogger extends \Thread implements Logger{
protected $logFile;
protected $logStream;
protected $shutdown;
protected $hasANSI;
private $logResource;
/** @var MainLogger */
public static $logger = null;
/**
* @param string $logFile
* @param bool $hasANSI
*
* @throws \RuntimeException
*/
public function __construct($logFile, $hasANSI = false){
if(static::$logger instanceof MainLogger){
throw new \RuntimeException("MainLogger has been already created");
}
static::$logger = $this;
@mkdir(basename($logFile), 0777, true);
$this->logFile = $logFile;
$this->hasANSI = (bool) $hasANSI;
$this->logStream = "";
$this->start(PTHREADS_INHERIT_NONE);
}
/**
* @return MainLogger
*/
public static function getLogger(){
return static::$logger;
}
public function emergency($message){
$this->send(TextFormat::RED . "[EMERGENCY] ". $message);
}
public function alert($message){
$this->send(TextFormat::RED . "[ALERT] ". $message);
}
public function critical($message){
$this->send(TextFormat::RED . "[CRITICAL] ". $message);
}
public function error($message){
$this->send(TextFormat::DARK_RED . "[ERROR] ". $message);
}
public function warning($message){
$this->send(TextFormat::YELLOW . "[WARNING] ". $message);
}
public function notice($message){
$this->send(TextFormat::AQUA . "[NOTICE] ". $message);
}
public function info($message){
$this->send(TextFormat::WHITE . "[INFO] ". $message);
}
public function debug($message){
$this->send(TextFormat::GRAY . "[DEBUG] ". $message);
}
public function log($level, $message){
switch($level){
case LogLevel::EMERGENCY:
$this->emergency($message);
break;
case LogLevel::ALERT:
$this->alert($message);
break;
case LogLevel::CRITICAL:
$this->critical($message);
break;
case LogLevel::ERROR:
$this->error($message);
break;
case LogLevel::WARNING:
$this->warning($message);
break;
case LogLevel::NOTICE:
$this->notice($message);
break;
case LogLevel::INFO:
$this->info($message);
break;
case LogLevel::DEBUG:
$this->debug($message);
break;
}
}
public function shutdown(){
$this->shutdown = true;
}
protected function send($message){
$now = time();
$message = TextFormat::toANSI(TextFormat::AQUA . date("H:i:s", $now) . TextFormat::RESET . " " . $message . TextFormat::RESET . PHP_EOL);
$cleanMessage = TextFormat::clean(preg_replace('/\x1b\[[0-9;]*m/', "", $message));
if(!$this->hasANSI){
echo $cleanMessage;
}else{
echo $message;
}
$this->logStream .= date("Y-m-d", $now) . " " . $cleanMessage;
}
public function run(){
$this->shutdown = false;
$this->logResource = fopen($this->logFile, "a+b");
if(!is_resource($this->logResource)){
throw new \RuntimeException("Couldn't open log file");
}
flock($this->logResource, LOCK_EX);
while($this->shutdown === false){
if(strlen($this->logStream) >= 4096){
$this->synchronized(function(){
$chunks = strlen($this->logStream) >> 12;
$chunk = substr($this->logStream, 0, $chunks << 12);
$this->logStream = substr($this->logStream, $chunks << 12);
fwrite($this->logResource, $chunk);
});
}else{
usleep(250000); //sleep for 0.25 seconds
}
}
if(strlen($this->logStream) > 0){
fwrite($this->logResource, $this->logStream);
}
flock($this->logResource, LOCK_UN);
fclose($this->logResource);
}
}

View File

@ -0,0 +1,163 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\utils;
class MainLogger extends \Thread implements Logger{
protected $logFile;
protected $logStream;
protected $shutdown;
protected $hasANSI;
private $logResource;
/** @var MainLogger */
public static $logger = null;
/**
* @param string $logFile
* @param bool $hasANSI
*
* @throws \RuntimeException
*/
public function __construct($logFile, $hasANSI = false){
if(static::$logger instanceof MainLogger){
throw new \RuntimeException("MainLogger has been already created");
}
static::$logger = $this;
@mkdir(basename($logFile), 0777, true);
$this->logFile = $logFile;
$this->hasANSI = (bool) $hasANSI;
$this->logStream = "";
$this->start(PTHREADS_INHERIT_NONE);
}
/**
* @return MainLogger
*/
public static function getLogger(){
return static::$logger;
}
public function emergency($message){
$this->send(TextFormat::RED . "[EMERGENCY] ". $message);
}
public function alert($message){
$this->send(TextFormat::RED . "[ALERT] ". $message);
}
public function critical($message){
$this->send(TextFormat::RED . "[CRITICAL] ". $message);
}
public function error($message){
$this->send(TextFormat::DARK_RED . "[ERROR] ". $message);
}
public function warning($message){
$this->send(TextFormat::YELLOW . "[WARNING] ". $message);
}
public function notice($message){
$this->send(TextFormat::AQUA . "[NOTICE] ". $message);
}
public function info($message){
$this->send(TextFormat::WHITE . "[INFO] ". $message);
}
public function debug($message){
$this->send(TextFormat::GRAY . "[DEBUG] ". $message);
}
public function log($level, $message){
switch($level){
case LogLevel::EMERGENCY:
$this->emergency($message);
break;
case LogLevel::ALERT:
$this->alert($message);
break;
case LogLevel::CRITICAL:
$this->critical($message);
break;
case LogLevel::ERROR:
$this->error($message);
break;
case LogLevel::WARNING:
$this->warning($message);
break;
case LogLevel::NOTICE:
$this->notice($message);
break;
case LogLevel::INFO:
$this->info($message);
break;
case LogLevel::DEBUG:
$this->debug($message);
break;
}
}
public function shutdown(){
$this->shutdown = true;
}
protected function send($message){
$now = time();
$message = TextFormat::toANSI(TextFormat::AQUA . date("H:i:s", $now) . TextFormat::RESET . " " . $message . TextFormat::RESET . PHP_EOL);
$cleanMessage = TextFormat::clean(preg_replace('/\x1b\[[0-9;]*m/', "", $message));
if(!$this->hasANSI){
echo $cleanMessage;
}else{
echo $message;
}
$this->logStream .= date("Y-m-d", $now) . " " . $cleanMessage;
}
public function run(){
$this->shutdown = false;
$this->logResource = fopen($this->logFile, "a+b");
if(!is_resource($this->logResource)){
throw new \RuntimeException("Couldn't open log file");
}
flock($this->logResource, LOCK_EX);
while($this->shutdown === false){
if(strlen($this->logStream) >= 4096){
$this->synchronized(function(){
$chunks = strlen($this->logStream) >> 12;
$chunk = substr($this->logStream, 0, $chunks << 12);
$this->logStream = substr($this->logStream, $chunks << 12);
fwrite($this->logResource, $chunk);
});
}else{
usleep(250000); //sleep for 0.25 seconds
}
}
if(strlen($this->logStream) > 0){
fwrite($this->logResource, $this->logStream);
}
flock($this->logResource, LOCK_UN);
fclose($this->logResource);
}
}