diff --git a/bin/analyzelogs.php b/bin/analyzelogs.php new file mode 100755 index 0000000..910f15b --- /dev/null +++ b/bin/analyzelogs.php @@ -0,0 +1,141 @@ +#!/usr/bin/env php + | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 of the License, or | + | (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see | + +--------------------------------------------------------------------------+ + | Author: Christian Mollekopf | + +--------------------------------------------------------------------------+ +*/ + + +define('RCUBE_INSTALL_PATH', realpath(dirname(__FILE__) . '/../') . '/'); + +// Define include path +$include_path = RCUBE_INSTALL_PATH . 'lib' . PATH_SEPARATOR; +$include_path .= RCUBE_INSTALL_PATH . 'lib/ext' . PATH_SEPARATOR; +$include_path .= ini_get('include_path'); +set_include_path($include_path); + +require_once "Syncroton/Command/ICommand.php"; +require_once "Syncroton/Command/Wbxml.php"; +require_once "Syncroton/Command/Sync.php"; +require_once "Syncroton/Command/Ping.php"; +require_once "Syncroton/Command/MoveItems.php"; + +$filename = $argv[1]; + +$content = file_get_contents($filename); + +// Split up the log files into chunks that hopefully match the commands +$parts = preg_split("/\[.*\]: " . preg_quote("[DEBUG] Syncroton_Server::handle::65 REQUEST METHOD: POST", '/') . "/", $content, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); + +function getStatusConstants($classname) { + $reflect = new ReflectionClass($classname); + $result = $reflect->getConstants(); + $result = array_filter($result, function ($val) { + return str_starts_with($val, "STATUS_"); + }, ARRAY_FILTER_USE_KEY); + $result = array_flip($result); + return $result; +} + +function explainStatus($command, $status) { + switch ($command) { + case "Ping": + $result = getStatusConstants("Syncroton_Command_Ping"); + return $result[$status] ?? "Unknown"; + case "Sync": + $result = getStatusConstants("Syncroton_Command_Sync"); + return $result[$status] ?? "Unknown"; + case "MoveItems": + $result = getStatusConstants("Syncroton_Command_MoveItems"); + return $result[$status] ?? "Unknown"; + } + return "Unkonwn"; +} + +foreach ($parts as $part) { + preg_match('/\[(.*)\]: /', $part, $matches); + $timestamp = $matches[1]; + + preg_match('/\[command\] => (.*)/', $part, $matches); + $command = $matches[1]; + + preg_match('/\(.*)\<\/Status\>/', $part, $matches); + $status = $matches[1] ?? null; + + $statusExplained = explainStatus($command, $status); + + print(" Command: ". str_pad($command, 10) . str_pad("\tStatus: $status ($statusExplained)", 45) . "\tTimestamp: $timestamp\n"); + if ($command == "Sync") { + // Find collections within this sync + // [25-Sep-2024 09:16:35.347730]: [INFO] Syncroton_Command_Sync::handle::221 SyncKey is 7301 Class: Email CollectionId: 38b950ebd62cd9a66929c89615d0fc04 + if (preg_match_all('/SyncKey is (.*) Class: (.*) CollectionId: (.*)/', $part, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { + foreach ($matches as $set) { + $offset = $set[0][1]; + $collectionId = $set[3][0]; + $class = $set[2][0]; + $synckey = $set[1][0]; + print(str_pad(" Collection: $collectionId ($class)", 58) . "\tSyncKey: $synckey\n"); + + //Find the offset for this collections messages + if (preg_match("/Processing $collectionId\.\.\./", $part, $match, PREG_OFFSET_CAPTURE, $offset)) { + // print_r($match); + $offset = $match[0][1] ?? null; + //Find the actual changes + if ($offset && preg_match('/found \(added\/changed\/deleted\) (.*)\/(.*)\/(.*) entries for sync from server to client/', $part, $changesMatch, PREG_OFFSET_CAPTURE, $offset)) { + // If the offset is too large we are looking at the next collection. + if ($changesMatch[0][1] - $offset < 200) { + print(" " . $changesMatch[0][0] . "\n"); + } + } + } + //TODO We could figure out what the diff per collection was in terms of synckey to the last sync + //TODO We could figure out what we actually return in the response compared to the detected changeset + //TODO Warn if a collection is repeatedly synced with the same synckey, but changes are detected. It may be stuck in a sync loop. + } + } + + // Detect entries that are being added from the client + if (preg_match_all('/found (.*) entries to be added on server/', $part, $matches)) { + foreach ($matches[0] ?? [] as $match) { + print(" " . $match. "\n"); + } + } + + if (preg_match_all('/found (.*) entries to be updated on server/', $part, $matches)) { + foreach ($matches[0] ?? [] as $match) { + print(" " . $match. "\n"); + } + } + + if (preg_match_all('/found (.*) entries to be deleted on server/', $part, $matches)) { + foreach ($matches[0] ?? [] as $match) { + print(" " . $match. "\n"); + } + } + } + //TODO on Sync: + //* number of Add/Change/Remove from client and from server + //* Synckey + //* list involved folders + //TODO on Sync: + //* Reason for interruption +} +