├── Applications └── Statistics │ ├── Config │ ├── Cache │ │ └── empty │ └── Config.php │ ├── Web │ ├── js │ │ ├── scripts.js │ │ └── html5shiv.js │ ├── img │ │ ├── favicon.png │ │ ├── glyphicons-halflings.png │ │ ├── glyphicons-halflings-white.png │ │ ├── apple-touch-icon-57-precomposed.png │ │ ├── apple-touch-icon-72-precomposed.png │ │ ├── apple-touch-icon-114-precomposed.png │ │ └── apple-touch-icon-144-precomposed.png │ ├── css │ │ └── style.css │ ├── config.php │ ├── less │ │ ├── breadcrumbs.less │ │ ├── component-animations.less │ │ ├── wells.less │ │ ├── thumbnails.less │ │ ├── close.less │ │ ├── utilities.less │ │ ├── jumbotron.less │ │ ├── media.less │ │ ├── pager.less │ │ ├── badges.less │ │ ├── labels.less │ │ ├── bootstrap.less │ │ ├── code.less │ │ ├── alerts.less │ │ ├── print.less │ │ ├── pagination.less │ │ ├── progress-bars.less │ │ ├── list-group.less │ │ ├── scaffolding.less │ │ ├── grid.less │ │ ├── tooltip.less │ │ ├── modals.less │ │ ├── popovers.less │ │ ├── input-groups.less │ │ ├── buttons.less │ │ ├── panels.less │ │ ├── dropdowns.less │ │ ├── tables.less │ │ ├── carousel.less │ │ ├── responsive-utilities.less │ │ ├── type.less │ │ ├── button-groups.less │ │ ├── navs.less │ │ ├── theme.less │ │ ├── normalize.less │ │ └── forms.less │ ├── _init.php │ └── index.php │ ├── README.md │ ├── Views │ ├── footer.tpl.php │ ├── log.tpl.php │ ├── header.tpl.php │ ├── setting.tpl.php │ ├── login.tpl.php │ ├── admin.tpl.php │ ├── statistic.tpl.php │ └── main.tpl.php │ ├── loader.php │ ├── Lib │ ├── Cache.php │ └── functions.php │ ├── start_web.php │ ├── start_provider.php │ ├── start_worker.php │ ├── start_finder.php │ ├── Modules │ ├── setting.php │ ├── logger.php │ ├── admin.php │ ├── statistic.php │ └── main.php │ ├── Protocols │ └── Statistic.php │ ├── Clients │ └── StatisticClient.php │ └── Bootstrap │ └── StatisticWorker.php ├── .gitignore ├── start_for_win.bat ├── .gitattributes ├── composer.json ├── start.php ├── README.md └── MIT-LICENSE.txt /Applications/Statistics/Config/Cache/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/js/scripts.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | .buildpath 3 | .project 4 | .settings 5 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/walkor/workerman-statistics/HEAD/Applications/Statistics/Web/img/favicon.png -------------------------------------------------------------------------------- /Applications/Statistics/README.md: -------------------------------------------------------------------------------- 1 | 2 | 统计模块 3 | ======= 4 | 5 | * 管理员用户名密码默认都为空,即不需要登录就可以查看监控数据 6 | * 如果需要登录验证,在applications/Statistics/Config/Config.php里面设置管理员密码 7 | 8 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/walkor/workerman-statistics/HEAD/Applications/Statistics/Web/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /Applications/Statistics/Views/footer.tpl.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/walkor/workerman-statistics/HEAD/Applications/Statistics/Web/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /start_for_win.bat: -------------------------------------------------------------------------------- 1 | php Applications\Statistics\start_web.php Applications\Statistics\start_worker.php Applications\Statistics\start_provider.php Applications\Statistics\start_finder.php 2 | pause -------------------------------------------------------------------------------- /Applications/Statistics/Web/img/apple-touch-icon-57-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/walkor/workerman-statistics/HEAD/Applications/Statistics/Web/img/apple-touch-icon-57-precomposed.png -------------------------------------------------------------------------------- /Applications/Statistics/Web/img/apple-touch-icon-72-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/walkor/workerman-statistics/HEAD/Applications/Statistics/Web/img/apple-touch-icon-72-precomposed.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | *.js linguist-language=PHP 3 | *.css linguist-language=PHP 4 | *.html linguist-language=PHP 5 | *.less linguist-language=PHP 6 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/img/apple-touch-icon-114-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/walkor/workerman-statistics/HEAD/Applications/Statistics/Web/img/apple-touch-icon-114-precomposed.png -------------------------------------------------------------------------------- /Applications/Statistics/Web/img/apple-touch-icon-144-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/walkor/workerman-statistics/HEAD/Applications/Statistics/Web/img/apple-touch-icon-144-precomposed.png -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "workerman/statistics", 3 | "type" : "project", 4 | "homepage": "http://www.workerman.net", 5 | "license" : "MIT", 6 | "require": { 7 | "workerman/workerman" : "~3.5.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/css/style.css: -------------------------------------------------------------------------------- 1 | body {margin: 10px 0px} 2 | .nav { 3 | margin-bottom: 10px; 4 | } 5 | .height-400{ 6 | height:400px 7 | } 8 | 9 | .footer { 10 | margin: 8px auto 0 auto; 11 | padding-bottom: 8px; 12 | color: #666; 13 | font-size: 12px; 14 | text-align: center; 15 | } 16 | 17 | .footer > a 18 | { 19 | font-size:bold; 20 | color:#333; 21 | } -------------------------------------------------------------------------------- /Applications/Statistics/Web/config.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Statistics\Web; 15 | class Config 16 | { 17 | // 数据源端口,会向这个端口发送udp广播获取ip,然后从这个端口以tcp协议获取统计信息 18 | public static $ProviderPort = 55858; 19 | } -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/breadcrumbs.less: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: 8px 15px; 8 | margin-bottom: @line-height-computed; 9 | list-style: none; 10 | background-color: @breadcrumb-bg; 11 | border-radius: @border-radius-base; 12 | > li { 13 | display: inline-block; 14 | + li:before { 15 | content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space 16 | padding: 0 5px; 17 | color: @breadcrumb-color; 18 | } 19 | } 20 | > .active { 21 | color: @breadcrumb-active-color; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/component-animations.less: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | // Heads up! 6 | // 7 | // We don't use the `.opacity()` mixin here since it causes a bug with text 8 | // fields in IE7-8. Source: https://github.com/twitter/bootstrap/pull/3552. 9 | 10 | .fade { 11 | opacity: 0; 12 | .transition(opacity .15s linear); 13 | &.in { 14 | opacity: 1; 15 | } 16 | } 17 | 18 | .collapse { 19 | display: none; 20 | &.in { 21 | display: block; 22 | } 23 | } 24 | .collapsing { 25 | position: relative; 26 | height: 0; 27 | overflow: hidden; 28 | .transition(height .35s ease); 29 | } 30 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/wells.less: -------------------------------------------------------------------------------- 1 | // 2 | // Wells 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .well { 8 | min-height: 20px; 9 | padding: 19px; 10 | margin-bottom: 20px; 11 | background-color: @well-bg; 12 | border: 1px solid darken(@well-bg, 7%); 13 | border-radius: @border-radius-base; 14 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 15 | blockquote { 16 | border-color: #ddd; 17 | border-color: rgba(0,0,0,.15); 18 | } 19 | } 20 | 21 | // Sizes 22 | .well-lg { 23 | padding: 24px; 24 | border-radius: @border-radius-large; 25 | } 26 | .well-sm { 27 | padding: 9px; 28 | border-radius: @border-radius-small; 29 | } 30 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/thumbnails.less: -------------------------------------------------------------------------------- 1 | // 2 | // Thumbnails 3 | // -------------------------------------------------- 4 | 5 | 6 | // Mixin and adjust the regular image class 7 | .thumbnail { 8 | .img-thumbnail(); 9 | display: block; // Override the inline-block from `.img-thumbnail` 10 | margin-bottom: @line-height-computed; 11 | 12 | > img { 13 | .img-responsive(); 14 | margin-left: auto; 15 | margin-right: auto; 16 | } 17 | } 18 | 19 | 20 | // Add a hover state for linked versions only 21 | a.thumbnail:hover, 22 | a.thumbnail:focus, 23 | a.thumbnail.active { 24 | border-color: @link-color; 25 | } 26 | 27 | // Image captions 28 | .thumbnail .caption { 29 | padding: @thumbnail-caption-padding; 30 | color: @thumbnail-caption-color; 31 | } 32 | -------------------------------------------------------------------------------- /start.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | 15 | require_once __DIR__ . '/../../vendor/autoload.php'; 16 | 17 | require_once __DIR__ .'/Config/Config.php'; 18 | require_once __DIR__.'/Protocols/Statistic.php'; 19 | require_once __DIR__.'/Bootstrap/StatisticProvider.php'; 20 | require_once __DIR__.'/Bootstrap/StatisticWorker.php'; 21 | -------------------------------------------------------------------------------- /Applications/Statistics/Lib/Cache.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Statistics\Lib; 15 | class Cache 16 | { 17 | public static $statisticDataCache = array(); 18 | public static $ServerIpList = array(); 19 | public static $modulesDataCache = array(); 20 | public static $lastFailedIpArray = array(); 21 | public static $lastSuccessIpArray = array(); 22 | } -------------------------------------------------------------------------------- /Applications/Statistics/Web/_init.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | define('ST_ROOT', realpath(__DIR__.'/../')); 15 | require_once ST_ROOT .'/Lib/functions.php'; 16 | require_once ST_ROOT .'/Lib/Cache.php'; 17 | require_once ST_ROOT .'/Config/Config.php'; 18 | // 覆盖配置文件 19 | foreach(glob(ST_ROOT . '/Config/Cache/*.php') as $php_file) 20 | { 21 | require_once $php_file; 22 | } -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/close.less: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: (@font-size-base * 1.5); 9 | font-weight: @close-font-weight; 10 | line-height: 1; 11 | color: @close-color; 12 | text-shadow: @close-text-shadow; 13 | .opacity(.2); 14 | 15 | &:hover, 16 | &:focus { 17 | color: @close-color; 18 | text-decoration: none; 19 | cursor: pointer; 20 | .opacity(.5); 21 | } 22 | 23 | // Additional properties for button version 24 | // iOS requires the button element instead of an anchor tag. 25 | // If you want the anchor version, it requires `href="#"`. 26 | button& { 27 | padding: 0; 28 | cursor: pointer; 29 | background: transparent; 30 | border: 0; 31 | -webkit-appearance: none; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Applications/Statistics/start_web.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | require_once __DIR__ . '/loader.php'; 15 | 16 | use \Workerman\Worker; 17 | use \Workerman\WebServer; 18 | 19 | // WebServer 20 | $web = new WebServer("http://0.0.0.0:55757"); 21 | $web->name = 'StatisticWeb'; 22 | $web->addRoot('www.your_domain.com', __DIR__.'/Web'); 23 | 24 | // 如果不是在根目录启动,则运行runAll方法 25 | if(!defined('GLOBAL_START')) 26 | { 27 | Worker::runAll(); 28 | } 29 | -------------------------------------------------------------------------------- /Applications/Statistics/Config/Config.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Statistics; 15 | class Config 16 | { 17 | // 数据源端口,会向这个端口发送udp广播获取ip,然后从这个端口以tcp协议获取统计信息 18 | public static $ProviderPort = 55858; 19 | 20 | // 管理员用户名,用户名密码都为空字符串时说明不用验证 21 | public static $adminName = ''; 22 | 23 | // 管理员密码,用户名密码都为空字符串时说明不用验证 24 | public static $adminPassword = ''; 25 | 26 | public static $dataPath = ''; 27 | } 28 | 29 | Config::$dataPath = __DIR__ . '/../data/'; -------------------------------------------------------------------------------- /Applications/Statistics/start_provider.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | require_once __DIR__ . '/loader.php'; 15 | 16 | use Bootstrap\StatisticProvider; 17 | use Bootstrap\StatisticWorker; 18 | use \Workerman\Worker; 19 | use \Workerman\WebServer; 20 | 21 | // StatisticProvider 22 | $statistic_provider = new StatisticProvider("Text://0.0.0.0:55858"); 23 | $statistic_provider->name = 'StatisticProvider'; 24 | 25 | // 如果不是在根目录启动,则运行runAll方法 26 | if(!defined('GLOBAL_START')) 27 | { 28 | Worker::runAll(); 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 所需环境 2 | ======== 3 | 4 | 需要PHP版本不低于5.3,只需要安装PHP的Cli即可,无需安装PHP-FPM、nginx、apache 5 | 6 | 7 | 示例 8 | ======== 9 | [Live Demo](http://demos.workerman.net:55757/) 10 | 11 | 安装 12 | ========= 13 | 1、下载 或者 ```git clone https://github.com/walkor/workerman-statistics``` 14 | 15 | 2、命令行运行 ```composer install``` 16 | 17 | 启动停止 18 | ========= 19 | 20 | 以ubuntu为例 21 | 22 | 启动 23 | `php start.php start -d` 24 | 25 | 重启启动 26 | `php start.php restart` 27 | 28 | 平滑重启/重新加载配置 29 | `php start.php reload` 30 | 31 | 查看服务状态 32 | `php start.php status` 33 | 34 | 停止 35 | `php start.php stop` 36 | 37 | Windows系统上运行 38 | ====== 39 | 1、Windows平台需要将Workerman目录替换成[Windows版本的Workerman](https://github.com/walkor/workerman-for-win) 40 | 41 | 2、运行start_for_win.bat 42 | 43 | [Windows版本Workerman相关参见这里](http://www.workerman.net/windows) 44 | 45 | 权限验证 46 | ======= 47 | 48 | * 管理员用户名密码默认都为空,即不需要登录就可以查看监控数据 49 | * 如果需要登录验证,在applications/Statistics/Config/Config.php里面设置管理员密码 50 | 51 | -------------------------------------------------------------------------------- /Applications/Statistics/start_worker.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | require_once __DIR__ . '/loader.php'; 15 | 16 | use Bootstrap\StatisticProvider; 17 | use Bootstrap\StatisticWorker; 18 | use \Workerman\Worker; 19 | use \Workerman\WebServer; 20 | 21 | // StatisticWorker 22 | $statistic_worker = new StatisticWorker("Statistic://0.0.0.0:55656"); 23 | $statistic_worker->transport = 'udp'; 24 | $statistic_worker->name = 'StatisticWorker'; 25 | 26 | // 如果不是在根目录启动,则运行runAll方法 27 | if(!defined('GLOBAL_START')) 28 | { 29 | Worker::runAll(); 30 | } 31 | -------------------------------------------------------------------------------- /Applications/Statistics/Views/log.tpl.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/utilities.less: -------------------------------------------------------------------------------- 1 | // 2 | // Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Floats 7 | // ------------------------- 8 | 9 | .clearfix { 10 | .clearfix(); 11 | } 12 | .center-block { 13 | .center-block(); 14 | } 15 | .pull-right { 16 | float: right !important; 17 | } 18 | .pull-left { 19 | float: left !important; 20 | } 21 | 22 | 23 | // Toggling content 24 | // ------------------------- 25 | 26 | // Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1 27 | .hide { 28 | display: none !important; 29 | } 30 | .show { 31 | display: block !important; 32 | } 33 | .invisible { 34 | visibility: hidden; 35 | } 36 | .text-hide { 37 | .text-hide(); 38 | } 39 | 40 | 41 | // Hide from screenreaders and browsers 42 | // 43 | // Credit: HTML5 Boilerplate 44 | 45 | .hidden { 46 | display: none !important; 47 | visibility: hidden !important; 48 | } 49 | 50 | 51 | // For Affix plugin 52 | // ------------------------- 53 | 54 | .affix { 55 | position: fixed; 56 | } 57 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/jumbotron.less: -------------------------------------------------------------------------------- 1 | // 2 | // Jumbotron 3 | // -------------------------------------------------- 4 | 5 | 6 | .jumbotron { 7 | padding: @jumbotron-padding; 8 | margin-bottom: @jumbotron-padding; 9 | font-size: @jumbotron-font-size; 10 | font-weight: 200; 11 | line-height: (@line-height-base * 1.5); 12 | color: @jumbotron-color; 13 | background-color: @jumbotron-bg; 14 | 15 | h1 { 16 | line-height: 1; 17 | color: @jumbotron-heading-color; 18 | } 19 | p { 20 | line-height: 1.4; 21 | } 22 | 23 | .container & { 24 | border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container 25 | } 26 | 27 | @media screen and (min-width: @screen-sm-min) { 28 | padding-top: (@jumbotron-padding * 1.6); 29 | padding-bottom: (@jumbotron-padding * 1.6); 30 | 31 | .container & { 32 | padding-left: (@jumbotron-padding * 2); 33 | padding-right: (@jumbotron-padding * 2); 34 | } 35 | 36 | h1 { 37 | font-size: (@font-size-base * 4.5); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/media.less: -------------------------------------------------------------------------------- 1 | // Media objects 2 | // Source: http://stubbornella.org/content/?p=497 3 | // -------------------------------------------------- 4 | 5 | 6 | // Common styles 7 | // ------------------------- 8 | 9 | // Clear the floats 10 | .media, 11 | .media-body { 12 | overflow: hidden; 13 | zoom: 1; 14 | } 15 | 16 | // Proper spacing between instances of .media 17 | .media, 18 | .media .media { 19 | margin-top: 15px; 20 | } 21 | .media:first-child { 22 | margin-top: 0; 23 | } 24 | 25 | // For images and videos, set to block 26 | .media-object { 27 | display: block; 28 | } 29 | 30 | // Reset margins on headings for tighter default spacing 31 | .media-heading { 32 | margin: 0 0 5px; 33 | } 34 | 35 | 36 | // Media image alignment 37 | // ------------------------- 38 | 39 | .media { 40 | > .pull-left { 41 | margin-right: 10px; 42 | } 43 | > .pull-right { 44 | margin-left: 10px; 45 | } 46 | } 47 | 48 | 49 | // Media list variation 50 | // ------------------------- 51 | 52 | // Undo default ul/ol styles 53 | .media-list { 54 | padding-left: 0; 55 | list-style: none; 56 | } 57 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/pager.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pager pagination 3 | // -------------------------------------------------- 4 | 5 | 6 | .pager { 7 | padding-left: 0; 8 | margin: @line-height-computed 0; 9 | list-style: none; 10 | text-align: center; 11 | .clearfix(); 12 | li { 13 | display: inline; 14 | > a, 15 | > span { 16 | display: inline-block; 17 | padding: 5px 14px; 18 | background-color: @pagination-bg; 19 | border: 1px solid @pagination-border; 20 | border-radius: @pager-border-radius; 21 | } 22 | 23 | > a:hover, 24 | > a:focus { 25 | text-decoration: none; 26 | background-color: @pagination-hover-bg; 27 | } 28 | } 29 | 30 | .next { 31 | > a, 32 | > span { 33 | float: right; 34 | } 35 | } 36 | 37 | .previous { 38 | > a, 39 | > span { 40 | float: left; 41 | } 42 | } 43 | 44 | .disabled { 45 | > a, 46 | > a:hover, 47 | > a:focus, 48 | > span { 49 | color: @pager-disabled-color; 50 | background-color: @pagination-bg; 51 | cursor: not-allowed; 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2009-2015 walkor and contributors (see https://github.com/walkor/workerman/contributors) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/badges.less: -------------------------------------------------------------------------------- 1 | // 2 | // Badges 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base classes 7 | .badge { 8 | display: inline-block; 9 | min-width: 10px; 10 | padding: 3px 7px; 11 | font-size: @font-size-small; 12 | font-weight: @badge-font-weight; 13 | color: @badge-color; 14 | line-height: @badge-line-height; 15 | vertical-align: baseline; 16 | white-space: nowrap; 17 | text-align: center; 18 | background-color: @badge-bg; 19 | border-radius: @badge-border-radius; 20 | 21 | // Empty badges collapse automatically (not available in IE8) 22 | &:empty { 23 | display: none; 24 | } 25 | } 26 | 27 | // Hover state, but only for links 28 | a.badge { 29 | &:hover, 30 | &:focus { 31 | color: @badge-link-hover-color; 32 | text-decoration: none; 33 | cursor: pointer; 34 | } 35 | } 36 | 37 | // Quick fix for labels/badges in buttons 38 | .btn .badge { 39 | position: relative; 40 | top: -1px; 41 | } 42 | 43 | // Account for counters in navs 44 | a.list-group-item.active > .badge, 45 | .nav-pills > .active > a > .badge { 46 | color: @badge-active-color; 47 | background-color: @badge-active-bg; 48 | } 49 | .nav-pills > li > a > .badge { 50 | margin-left: 3px; 51 | } 52 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/labels.less: -------------------------------------------------------------------------------- 1 | // 2 | // Labels 3 | // -------------------------------------------------- 4 | 5 | .label { 6 | display: inline; 7 | padding: .2em .6em .3em; 8 | font-size: 75%; 9 | font-weight: bold; 10 | line-height: 1; 11 | color: @label-color; 12 | text-align: center; 13 | white-space: nowrap; 14 | vertical-align: baseline; 15 | border-radius: .25em; 16 | 17 | // Add hover effects, but only for links 18 | &[href] { 19 | &:hover, 20 | &:focus { 21 | color: @label-link-hover-color; 22 | text-decoration: none; 23 | cursor: pointer; 24 | } 25 | } 26 | 27 | // Empty labels collapse automatically (not available in IE8) 28 | &:empty { 29 | display: none; 30 | } 31 | } 32 | 33 | // Colors 34 | // Contextual variations (linked labels get darker on :hover) 35 | 36 | .label-default { 37 | .label-variant(@label-default-bg); 38 | } 39 | 40 | .label-primary { 41 | .label-variant(@label-primary-bg); 42 | } 43 | 44 | .label-success { 45 | .label-variant(@label-success-bg); 46 | } 47 | 48 | .label-info { 49 | .label-variant(@label-info-bg); 50 | } 51 | 52 | .label-warning { 53 | .label-variant(@label-warning-bg); 54 | } 55 | 56 | .label-danger { 57 | .label-variant(@label-danger-bg); 58 | } 59 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/bootstrap.less: -------------------------------------------------------------------------------- 1 | // Core variables and mixins 2 | @import "variables.less"; 3 | @import "mixins.less"; 4 | 5 | // Reset 6 | @import "normalize.less"; 7 | @import "print.less"; 8 | 9 | // Core CSS 10 | @import "scaffolding.less"; 11 | @import "type.less"; 12 | @import "code.less"; 13 | @import "grid.less"; 14 | @import "tables.less"; 15 | @import "forms.less"; 16 | @import "buttons.less"; 17 | 18 | // Components 19 | @import "component-animations.less"; 20 | @import "glyphicons.less"; 21 | @import "dropdowns.less"; 22 | @import "button-groups.less"; 23 | @import "input-groups.less"; 24 | @import "navs.less"; 25 | @import "navbar.less"; 26 | @import "breadcrumbs.less"; 27 | @import "pagination.less"; 28 | @import "pager.less"; 29 | @import "labels.less"; 30 | @import "badges.less"; 31 | @import "jumbotron.less"; 32 | @import "thumbnails.less"; 33 | @import "alerts.less"; 34 | @import "progress-bars.less"; 35 | @import "media.less"; 36 | @import "list-group.less"; 37 | @import "panels.less"; 38 | @import "wells.less"; 39 | @import "close.less"; 40 | 41 | // Components w/ JavaScript 42 | @import "modals.less"; 43 | @import "tooltip.less"; 44 | @import "popovers.less"; 45 | @import "carousel.less"; 46 | 47 | // Utility classes 48 | @import "utilities.less"; 49 | @import "responsive-utilities.less"; 50 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/code.less: -------------------------------------------------------------------------------- 1 | // 2 | // Code (inline and block) 3 | // -------------------------------------------------- 4 | 5 | 6 | // Inline and block code styles 7 | code, 8 | kbd, 9 | pre, 10 | samp { 11 | font-family: @font-family-monospace; 12 | } 13 | 14 | // Inline code 15 | code { 16 | padding: 2px 4px; 17 | font-size: 90%; 18 | color: @code-color; 19 | background-color: @code-bg; 20 | white-space: nowrap; 21 | border-radius: @border-radius-base; 22 | } 23 | 24 | // Blocks of code 25 | pre { 26 | display: block; 27 | padding: ((@line-height-computed - 1) / 2); 28 | margin: 0 0 (@line-height-computed / 2); 29 | font-size: (@font-size-base - 1); // 14px to 13px 30 | line-height: @line-height-base; 31 | word-break: break-all; 32 | word-wrap: break-word; 33 | color: @pre-color; 34 | background-color: @pre-bg; 35 | border: 1px solid @pre-border-color; 36 | border-radius: @border-radius-base; 37 | 38 | // Account for some code outputs that place code tags in pre tags 39 | code { 40 | padding: 0; 41 | font-size: inherit; 42 | color: inherit; 43 | white-space: pre-wrap; 44 | background-color: transparent; 45 | border-radius: 0; 46 | } 47 | } 48 | 49 | // Enable scrollable blocks of code 50 | .pre-scrollable { 51 | max-height: @pre-scrollable-max-height; 52 | overflow-y: scroll; 53 | } 54 | -------------------------------------------------------------------------------- /Applications/Statistics/start_finder.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | require_once __DIR__ . '/loader.php'; 15 | 16 | use Bootstrap\StatisticProvider; 17 | use Bootstrap\StatisticWorker; 18 | use \Workerman\Worker; 19 | use \Workerman\WebServer; 20 | 21 | // recv udp broadcast 22 | $udp_finder = new Worker("Text://0.0.0.0:55858"); 23 | $udp_finder->name = 'StatisticFinder'; 24 | $udp_finder->transport = 'udp'; 25 | $udp_finder->onMessage = function ($connection, $data) 26 | { 27 | $data = json_decode($data, true); 28 | if(empty($data)) 29 | { 30 | return false; 31 | } 32 | 33 | // 无法解析的包 34 | if(empty($data['cmd']) || $data['cmd'] != 'REPORT_IP' ) 35 | { 36 | return false; 37 | } 38 | 39 | // response 40 | return $connection->send(json_encode(array('result'=>'ok'))); 41 | }; 42 | 43 | // 如果不是在根目录启动,则运行runAll方法 44 | if(!defined('GLOBAL_START')) 45 | { 46 | Worker::runAll(); 47 | } 48 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/index.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | require_once __DIR__.'/_init.php'; 15 | 16 | // 检查是否登录 17 | check_auth(); 18 | 19 | $func = isset($_GET['fn']) ? $_GET['fn'] : 'main'; 20 | $func = "\\Statistics\\Modules\\".$func; 21 | if(!function_exists($func)) 22 | { 23 | foreach(glob(ST_ROOT . "/Modules/*") as $php_file) 24 | { 25 | require_once $php_file; 26 | } 27 | } 28 | 29 | if(!function_exists($func)) 30 | { 31 | $func = "\\Statistics\\Modules\\main"; 32 | } 33 | 34 | $module = isset($_GET['module']) ? $_GET['module'] : ''; 35 | $interface = isset($_GET['interface']) ? $_GET['interface'] : ''; 36 | $date = isset($_GET['date']) ? $_GET['date'] : date('Y-m-d'); 37 | $start_time = isset($_GET['start_time']) ? $_GET['start_time'] : strtotime(date('Y-m-d')); 38 | $offset = isset($_GET['offset']) ? $_GET['offset'] : 0; 39 | $log_count_per_ip = $log_count_per_page = 40; 40 | if(empty($_GET['count']) && $ip_count = count(\Statistics\Lib\Cache::$ServerIpList)) 41 | { 42 | $log_count_per_ip = ceil($log_count_per_page/$ip_count); 43 | } 44 | call_user_func_array($func, array($module, $interface, $date, $start_time, $offset, $log_count_per_ip)); -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/alerts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Alerts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // ------------------------- 8 | 9 | .alert { 10 | padding: @alert-padding; 11 | margin-bottom: @line-height-computed; 12 | border: 1px solid transparent; 13 | border-radius: @alert-border-radius; 14 | 15 | // Headings for larger alerts 16 | h4 { 17 | margin-top: 0; 18 | // Specified for the h4 to prevent conflicts of changing @headings-color 19 | color: inherit; 20 | } 21 | // Provide class for links that match alerts 22 | .alert-link { 23 | font-weight: @alert-link-font-weight; 24 | } 25 | 26 | // Improve alignment and spacing of inner content 27 | > p, 28 | > ul { 29 | margin-bottom: 0; 30 | } 31 | > p + p { 32 | margin-top: 5px; 33 | } 34 | } 35 | 36 | // Dismissable alerts 37 | // 38 | // Expand the right padding and account for the close button's positioning. 39 | 40 | .alert-dismissable { 41 | padding-right: (@alert-padding + 20); 42 | 43 | // Adjust close link position 44 | .close { 45 | position: relative; 46 | top: -2px; 47 | right: -21px; 48 | color: inherit; 49 | } 50 | } 51 | 52 | // Alternate styles 53 | // 54 | // Generate contextual modifier classes for colorizing the alert. 55 | 56 | .alert-success { 57 | .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text); 58 | } 59 | .alert-info { 60 | .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text); 61 | } 62 | .alert-warning { 63 | .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text); 64 | } 65 | .alert-danger { 66 | .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text); 67 | } 68 | -------------------------------------------------------------------------------- /Applications/Statistics/Views/header.tpl.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WorkerMan-集群统计与监控 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Applications/Statistics/Modules/setting.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Statistics\Modules; 15 | 16 | function setting() 17 | { 18 | $act = isset($_GET['act'])? $_GET['act'] : 'home'; 19 | $err_msg = $notice_msg = $suc_msg = $ip_list_str = ''; 20 | switch($act) 21 | { 22 | case 'save': 23 | if(empty($_POST['detect_port'])) 24 | { 25 | $err_msg = "探测端口不能为空"; 26 | break; 27 | } 28 | $detect_port = (int)$_POST['detect_port']; 29 | 30 | if($detect_port<0 || $detect_port > 65535) 31 | { 32 | $err_msg = "探测端口不合法"; 33 | break; 34 | } 35 | $suc_msg = "保存成功"; 36 | \Statistics\Config::$ProviderPort = $detect_port; 37 | saveDetectPortToCache(); 38 | break; 39 | default: 40 | $detect_port = \Statistics\Config::$ProviderPort; 41 | } 42 | 43 | include ST_ROOT . '/Views/header.tpl.php'; 44 | include ST_ROOT . '/Views/setting.tpl.php'; 45 | include ST_ROOT . '/Views/footer.tpl.php'; 46 | } 47 | 48 | function saveDetectPortToCache() 49 | { 50 | foreach(glob(ST_ROOT . '/Config/Cache/*detect_port.cache.php') as $php_file) 51 | { 52 | unlink($php_file); 53 | } 54 | file_put_contents(ST_ROOT . '/Config/Cache/'.time().'.detect_port.cache.php', " .btn { 87 | > .caret { 88 | border-top-color: #000 !important; 89 | } 90 | } 91 | .label { 92 | border: 1px solid #000; 93 | } 94 | 95 | .table { 96 | border-collapse: collapse !important; 97 | } 98 | .table-bordered { 99 | th, 100 | td { 101 | border: 1px solid #ddd !important; 102 | } 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/pagination.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pagination (multiple pages) 3 | // -------------------------------------------------- 4 | .pagination { 5 | display: inline-block; 6 | padding-left: 0; 7 | margin: @line-height-computed 0; 8 | border-radius: @border-radius-base; 9 | 10 | > li { 11 | display: inline; // Remove list-style and block-level defaults 12 | > a, 13 | > span { 14 | position: relative; 15 | float: left; // Collapse white-space 16 | padding: @padding-base-vertical @padding-base-horizontal; 17 | line-height: @line-height-base; 18 | text-decoration: none; 19 | background-color: @pagination-bg; 20 | border: 1px solid @pagination-border; 21 | margin-left: -1px; 22 | } 23 | &:first-child { 24 | > a, 25 | > span { 26 | margin-left: 0; 27 | .border-left-radius(@border-radius-base); 28 | } 29 | } 30 | &:last-child { 31 | > a, 32 | > span { 33 | .border-right-radius(@border-radius-base); 34 | } 35 | } 36 | } 37 | 38 | > li > a, 39 | > li > span { 40 | &:hover, 41 | &:focus { 42 | background-color: @pagination-hover-bg; 43 | } 44 | } 45 | 46 | > .active > a, 47 | > .active > span { 48 | &, 49 | &:hover, 50 | &:focus { 51 | z-index: 2; 52 | color: @pagination-active-color; 53 | background-color: @pagination-active-bg; 54 | border-color: @pagination-active-bg; 55 | cursor: default; 56 | } 57 | } 58 | 59 | > .disabled { 60 | > span, 61 | > span:hover, 62 | > span:focus, 63 | > a, 64 | > a:hover, 65 | > a:focus { 66 | color: @pagination-disabled-color; 67 | background-color: @pagination-bg; 68 | border-color: @pagination-border; 69 | cursor: not-allowed; 70 | } 71 | } 72 | } 73 | 74 | // Sizing 75 | // -------------------------------------------------- 76 | 77 | // Large 78 | .pagination-lg { 79 | .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @border-radius-large); 80 | } 81 | 82 | // Small 83 | .pagination-sm { 84 | .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @border-radius-small); 85 | } 86 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/progress-bars.less: -------------------------------------------------------------------------------- 1 | // 2 | // Progress bars 3 | // -------------------------------------------------- 4 | 5 | 6 | // Bar animations 7 | // ------------------------- 8 | 9 | // WebKit 10 | @-webkit-keyframes progress-bar-stripes { 11 | from { background-position: 40px 0; } 12 | to { background-position: 0 0; } 13 | } 14 | 15 | // Firefox 16 | @-moz-keyframes progress-bar-stripes { 17 | from { background-position: 40px 0; } 18 | to { background-position: 0 0; } 19 | } 20 | 21 | // Opera 22 | @-o-keyframes progress-bar-stripes { 23 | from { background-position: 0 0; } 24 | to { background-position: 40px 0; } 25 | } 26 | 27 | // Spec and IE10+ 28 | @keyframes progress-bar-stripes { 29 | from { background-position: 40px 0; } 30 | to { background-position: 0 0; } 31 | } 32 | 33 | 34 | 35 | // Bar itself 36 | // ------------------------- 37 | 38 | // Outer container 39 | .progress { 40 | overflow: hidden; 41 | height: @line-height-computed; 42 | margin-bottom: @line-height-computed; 43 | background-color: @progress-bg; 44 | border-radius: @border-radius-base; 45 | .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); 46 | } 47 | 48 | // Bar of progress 49 | .progress-bar { 50 | float: left; 51 | width: 0%; 52 | height: 100%; 53 | font-size: @font-size-small; 54 | line-height: @line-height-computed; 55 | color: @progress-bar-color; 56 | text-align: center; 57 | background-color: @progress-bar-bg; 58 | .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); 59 | .transition(width .6s ease); 60 | } 61 | 62 | // Striped bars 63 | .progress-striped .progress-bar { 64 | #gradient > .striped(); 65 | background-size: 40px 40px; 66 | } 67 | 68 | // Call animation for the active one 69 | .progress.active .progress-bar { 70 | .animation(progress-bar-stripes 2s linear infinite); 71 | } 72 | 73 | 74 | 75 | // Variations 76 | // ------------------------- 77 | 78 | .progress-bar-success { 79 | .progress-bar-variant(@progress-bar-success-bg); 80 | } 81 | 82 | .progress-bar-info { 83 | .progress-bar-variant(@progress-bar-info-bg); 84 | } 85 | 86 | .progress-bar-warning { 87 | .progress-bar-variant(@progress-bar-warning-bg); 88 | } 89 | 90 | .progress-bar-danger { 91 | .progress-bar-variant(@progress-bar-danger-bg); 92 | } 93 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/list-group.less: -------------------------------------------------------------------------------- 1 | // 2 | // List groups 3 | // -------------------------------------------------- 4 | 5 | // Base class 6 | // 7 | // Easily usable on
    ,
      , or
      . 8 | .list-group { 9 | // No need to set list-style: none; since .list-group-item is block level 10 | margin-bottom: 20px; 11 | padding-left: 0; // reset padding because ul and ol 12 | } 13 | 14 | // Individual list items 15 | // ------------------------- 16 | 17 | .list-group-item { 18 | position: relative; 19 | display: block; 20 | padding: 10px 15px; 21 | // Place the border on the list items and negative margin up for better styling 22 | margin-bottom: -1px; 23 | background-color: @list-group-bg; 24 | border: 1px solid @list-group-border; 25 | 26 | // Round the first and last items 27 | &:first-child { 28 | .border-top-radius(@list-group-border-radius); 29 | } 30 | &:last-child { 31 | margin-bottom: 0; 32 | .border-bottom-radius(@list-group-border-radius); 33 | } 34 | 35 | // Align badges within list items 36 | > .badge { 37 | float: right; 38 | } 39 | > .badge + .badge { 40 | margin-right: 5px; 41 | } 42 | } 43 | 44 | // Linked list items 45 | a.list-group-item { 46 | color: @list-group-link-color; 47 | 48 | .list-group-item-heading { 49 | color: @list-group-link-heading-color; 50 | } 51 | 52 | // Hover state 53 | &:hover, 54 | &:focus { 55 | text-decoration: none; 56 | background-color: @list-group-hover-bg; 57 | } 58 | 59 | // Active class on item itself, not parent 60 | &.active, 61 | &.active:hover, 62 | &.active:focus { 63 | z-index: 2; // Place active items above their siblings for proper border styling 64 | color: @list-group-active-color; 65 | background-color: @list-group-active-bg; 66 | border-color: @list-group-active-border; 67 | 68 | // Force color to inherit for custom content 69 | .list-group-item-heading { 70 | color: inherit; 71 | } 72 | .list-group-item-text { 73 | color: lighten(@list-group-active-bg, 40%); 74 | } 75 | } 76 | } 77 | 78 | // Custom content options 79 | // ------------------------- 80 | 81 | .list-group-item-heading { 82 | margin-top: 0; 83 | margin-bottom: 5px; 84 | } 85 | .list-group-item-text { 86 | margin-bottom: 0; 87 | line-height: 1.3; 88 | } 89 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/js/html5shiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.6.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup main mark meter nav output progress section summary time video",version:"3.6.2",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment(); 8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d 2 |
      3 |
      4 | 32 |
      33 |
      34 |
      35 |
      36 | 44 | 45 |
      46 | 47 | 48 |
      49 | 50 |
      51 | 52 | 53 |
      54 | 55 |
      56 | 57 | 58 |
      59 | 60 |
      61 |
      62 |
      63 |
      64 |
      65 |
      66 |
      67 |
      68 | 69 |
      70 | 71 |
      72 |
      73 |
      74 |
      75 | 76 |
      77 |
      78 |
      79 |
      80 |
      81 |
      82 |
      83 |
      84 | -------------------------------------------------------------------------------- /Applications/Statistics/Views/login.tpl.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WorkerMan-集群统计与监控 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
      38 |
      39 |
      40 |
      41 |
      42 | 43 |
      44 | 45 |

      46 | 47 |

      48 |
      49 | 50 |

      workerman管理员登录

      51 |
      52 |
      53 | 54 |
      55 |
      56 | 57 |
      58 | 59 |
      60 |
      61 |
      62 |
      63 |
      64 |
      65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/tooltip.less: -------------------------------------------------------------------------------- 1 | // 2 | // Tooltips 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .tooltip { 8 | position: absolute; 9 | z-index: @zindex-tooltip; 10 | display: block; 11 | visibility: visible; 12 | font-size: @font-size-small; 13 | line-height: 1.4; 14 | .opacity(0); 15 | 16 | &.in { .opacity(.9); } 17 | &.top { margin-top: -3px; padding: @tooltip-arrow-width 0; } 18 | &.right { margin-left: 3px; padding: 0 @tooltip-arrow-width; } 19 | &.bottom { margin-top: 3px; padding: @tooltip-arrow-width 0; } 20 | &.left { margin-left: -3px; padding: 0 @tooltip-arrow-width; } 21 | } 22 | 23 | // Wrapper for the tooltip content 24 | .tooltip-inner { 25 | max-width: @tooltip-max-width; 26 | padding: 3px 8px; 27 | color: @tooltip-color; 28 | text-align: center; 29 | text-decoration: none; 30 | background-color: @tooltip-bg; 31 | border-radius: @border-radius-base; 32 | } 33 | 34 | // Arrows 35 | .tooltip-arrow { 36 | position: absolute; 37 | width: 0; 38 | height: 0; 39 | border-color: transparent; 40 | border-style: solid; 41 | } 42 | .tooltip { 43 | &.top .tooltip-arrow { 44 | bottom: 0; 45 | left: 50%; 46 | margin-left: -@tooltip-arrow-width; 47 | border-width: @tooltip-arrow-width @tooltip-arrow-width 0; 48 | border-top-color: @tooltip-arrow-color; 49 | } 50 | &.top-left .tooltip-arrow { 51 | bottom: 0; 52 | left: @tooltip-arrow-width; 53 | border-width: @tooltip-arrow-width @tooltip-arrow-width 0; 54 | border-top-color: @tooltip-arrow-color; 55 | } 56 | &.top-right .tooltip-arrow { 57 | bottom: 0; 58 | right: @tooltip-arrow-width; 59 | border-width: @tooltip-arrow-width @tooltip-arrow-width 0; 60 | border-top-color: @tooltip-arrow-color; 61 | } 62 | &.right .tooltip-arrow { 63 | top: 50%; 64 | left: 0; 65 | margin-top: -@tooltip-arrow-width; 66 | border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0; 67 | border-right-color: @tooltip-arrow-color; 68 | } 69 | &.left .tooltip-arrow { 70 | top: 50%; 71 | right: 0; 72 | margin-top: -@tooltip-arrow-width; 73 | border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width; 74 | border-left-color: @tooltip-arrow-color; 75 | } 76 | &.bottom .tooltip-arrow { 77 | top: 0; 78 | left: 50%; 79 | margin-left: -@tooltip-arrow-width; 80 | border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; 81 | border-bottom-color: @tooltip-arrow-color; 82 | } 83 | &.bottom-left .tooltip-arrow { 84 | top: 0; 85 | left: @tooltip-arrow-width; 86 | border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; 87 | border-bottom-color: @tooltip-arrow-color; 88 | } 89 | &.bottom-right .tooltip-arrow { 90 | top: 0; 91 | right: @tooltip-arrow-width; 92 | border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; 93 | border-bottom-color: @tooltip-arrow-color; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Applications/Statistics/Views/admin.tpl.php: -------------------------------------------------------------------------------- 1 |
      2 |
      3 |
      4 | 32 |
      33 |
      34 |
      35 |
      36 | 44 | 45 |
      46 | 47 | 48 |
      49 | 50 |
      51 | 52 | 53 |
      54 | 55 |
      56 | 57 | 58 |
      59 | 60 |
      61 |
      62 |
      63 |
      64 |
      65 |
      66 | 67 |
      68 |
      69 | 70 |
      71 |
      72 |
      73 | 74 |
      75 |
      76 |
      77 | 78 | 返回主页 继续添加 79 | 80 |
      81 |
      82 |
      83 |
      84 |
      85 | -------------------------------------------------------------------------------- /Applications/Statistics/Modules/logger.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Statistics\Modules; 15 | function logger($module, $interface, $date, $start_time, $offset, $count) 16 | { 17 | $module_str =''; 18 | foreach(\Statistics\Lib\Cache::$modulesDataCache as $mod => $interfaces) 19 | { 20 | if($mod == 'WorkerMan') 21 | { 22 | continue; 23 | } 24 | $module_str .= '
    1. '.$mod.'
    2. '; 25 | if($module == $mod) 26 | { 27 | foreach ($interfaces as $if) 28 | { 29 | $module_str .= '
    3.   '.$if.'
    4. '; 30 | } 31 | } 32 | } 33 | 34 | $log_data_arr = getStasticLog($module, $interface, $start_time ,$offset, $count); 35 | unset($_GET['fn'], $_GET['ip'], $_GET['offset']); 36 | $log_str = ''; 37 | foreach($log_data_arr as $address => $log_data) 38 | { 39 | list($ip, $port) = explode(':', $address); 40 | $log_str .= $log_data['data']; 41 | $_GET['ip'][] = $ip; 42 | $_GET['offset'][] = $log_data['offset']; 43 | } 44 | $log_str = nl2br(str_replace("\n", "\n\n", $log_str)); 45 | $next_page_url = http_build_query($_GET); 46 | $log_str .= "
      下一页
      "; 47 | 48 | include ST_ROOT . '/Views/header.tpl.php'; 49 | include ST_ROOT . '/Views/log.tpl.php'; 50 | include ST_ROOT . '/Views/footer.tpl.php'; 51 | } 52 | 53 | function getStasticLog($module, $interface , $start_time, $offset = '', $count = 10) 54 | { 55 | $ip_list = (!empty($_GET['ip']) && is_array($_GET['ip'])) ? $_GET['ip'] : \Statistics\Lib\Cache::$ServerIpList; 56 | $offset_list = (!empty($_GET['offset']) && is_array($_GET['offset'])) ? $_GET['offset'] : array(); 57 | $port = \Statistics\Config::$ProviderPort; 58 | $request_buffer_array = array(); 59 | foreach($ip_list as $key=>$ip) 60 | { 61 | $offset = isset($offset_list[$key]) ? $offset_list[$key] : 0; 62 | $request_buffer_array["$ip:$port"] = json_encode(array('cmd'=>'get_log', 'module'=>$module, 'interface'=>$interface, 'start_time'=>$start_time, 'offset'=>$offset, 'count'=>$count))."\n"; 63 | } 64 | 65 | $read_buffer_array = multiRequest($request_buffer_array); 66 | ksort($read_buffer_array); 67 | foreach($read_buffer_array as $address => $buf) 68 | { 69 | list($ip, $port) = explode(':', $address); 70 | $body_data = json_decode(trim($buf), true); 71 | $log_data = isset($body_data['data']) ? $body_data['data'] : ''; 72 | $offset = isset($body_data['offset']) ? $body_data['offset'] : 0; 73 | $read_buffer_array[$address] = array('offset'=>$offset,'data'=>$log_data); 74 | } 75 | return $read_buffer_array; 76 | } 77 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/modals.less: -------------------------------------------------------------------------------- 1 | // 2 | // Modals 3 | // -------------------------------------------------- 4 | 5 | // .modal-open - body class for killing the scroll 6 | // .modal - container to scroll within 7 | // .modal-dialog - positioning shell for the actual modal 8 | // .modal-content - actual modal w/ bg and corners and shit 9 | 10 | // Kill the scroll on the body 11 | .modal-open { 12 | overflow: hidden; 13 | } 14 | 15 | // Container that the modal scrolls within 16 | .modal { 17 | display: none; 18 | overflow: auto; 19 | overflow-y: scroll; 20 | position: fixed; 21 | top: 0; 22 | right: 0; 23 | bottom: 0; 24 | left: 0; 25 | z-index: @zindex-modal-background; 26 | 27 | // When fading in the modal, animate it to slide down 28 | &.fade .modal-dialog { 29 | .translate(0, -25%); 30 | .transition-transform(~"0.3s ease-out"); 31 | } 32 | &.in .modal-dialog { .translate(0, 0)} 33 | } 34 | 35 | // Shell div to position the modal with bottom padding 36 | .modal-dialog { 37 | position: relative; 38 | margin-left: auto; 39 | margin-right: auto; 40 | width: auto; 41 | padding: 10px; 42 | z-index: (@zindex-modal-background + 10); 43 | } 44 | 45 | // Actual modal 46 | .modal-content { 47 | position: relative; 48 | background-color: @modal-content-bg; 49 | border: 1px solid @modal-content-fallback-border-color; //old browsers fallback (ie8 etc) 50 | border: 1px solid @modal-content-border-color; 51 | border-radius: @border-radius-large; 52 | .box-shadow(0 3px 9px rgba(0,0,0,.5)); 53 | background-clip: padding-box; 54 | // Remove focus outline from opened modal 55 | outline: none; 56 | } 57 | 58 | // Modal background 59 | .modal-backdrop { 60 | position: fixed; 61 | top: 0; 62 | right: 0; 63 | bottom: 0; 64 | left: 0; 65 | z-index: (@zindex-modal-background - 10); 66 | background-color: @modal-backdrop-bg; 67 | // Fade for backdrop 68 | &.fade { .opacity(0); } 69 | &.in { .opacity(.5); } 70 | } 71 | 72 | // Modal header 73 | // Top section of the modal w/ title and dismiss 74 | .modal-header { 75 | padding: @modal-title-padding; 76 | border-bottom: 1px solid @modal-header-border-color; 77 | min-height: (@modal-title-padding + @modal-title-line-height); 78 | } 79 | // Close icon 80 | .modal-header .close { 81 | margin-top: -2px; 82 | } 83 | 84 | // Title text within header 85 | .modal-title { 86 | margin: 0; 87 | line-height: @modal-title-line-height; 88 | } 89 | 90 | // Modal body 91 | // Where all modal content resides (sibling of .modal-header and .modal-footer) 92 | .modal-body { 93 | position: relative; 94 | padding: @modal-inner-padding; 95 | } 96 | 97 | // Footer (for actions) 98 | .modal-footer { 99 | margin-top: 15px; 100 | padding: (@modal-inner-padding - 1) @modal-inner-padding @modal-inner-padding; 101 | text-align: right; // right align buttons 102 | border-top: 1px solid @modal-footer-border-color; 103 | .clearfix(); // clear it in case folks use .pull-* classes on buttons 104 | 105 | // Properly space out buttons 106 | .btn + .btn { 107 | margin-left: 5px; 108 | margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs 109 | } 110 | // but override that for button groups 111 | .btn-group .btn + .btn { 112 | margin-left: -1px; 113 | } 114 | // and override it for block buttons as well 115 | .btn-block + .btn-block { 116 | margin-left: 0; 117 | } 118 | } 119 | 120 | // Scale up the modal 121 | @media screen and (min-width: @screen-sm-min) { 122 | 123 | .modal-dialog { 124 | width: 600px; 125 | padding-top: 30px; 126 | padding-bottom: 30px; 127 | } 128 | .modal-content { 129 | .box-shadow(0 5px 15px rgba(0,0,0,.5)); 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/popovers.less: -------------------------------------------------------------------------------- 1 | // 2 | // Popovers 3 | // -------------------------------------------------- 4 | 5 | 6 | .popover { 7 | position: absolute; 8 | top: 0; 9 | left: 0; 10 | z-index: @zindex-popover; 11 | display: none; 12 | max-width: @popover-max-width; 13 | padding: 1px; 14 | text-align: left; // Reset given new insertion method 15 | background-color: @popover-bg; 16 | background-clip: padding-box; 17 | border: 1px solid @popover-fallback-border-color; 18 | border: 1px solid @popover-border-color; 19 | border-radius: @border-radius-large; 20 | .box-shadow(0 5px 10px rgba(0,0,0,.2)); 21 | 22 | // Overrides for proper insertion 23 | white-space: normal; 24 | 25 | // Offset the popover to account for the popover arrow 26 | &.top { margin-top: -10px; } 27 | &.right { margin-left: 10px; } 28 | &.bottom { margin-top: 10px; } 29 | &.left { margin-left: -10px; } 30 | } 31 | 32 | .popover-title { 33 | margin: 0; // reset heading margin 34 | padding: 8px 14px; 35 | font-size: @font-size-base; 36 | font-weight: normal; 37 | line-height: 18px; 38 | background-color: @popover-title-bg; 39 | border-bottom: 1px solid darken(@popover-title-bg, 5%); 40 | border-radius: 5px 5px 0 0; 41 | } 42 | 43 | .popover-content { 44 | padding: 9px 14px; 45 | } 46 | 47 | // Arrows 48 | // 49 | // .arrow is outer, .arrow:after is inner 50 | 51 | .popover .arrow { 52 | &, 53 | &:after { 54 | position: absolute; 55 | display: block; 56 | width: 0; 57 | height: 0; 58 | border-color: transparent; 59 | border-style: solid; 60 | } 61 | } 62 | .popover .arrow { 63 | border-width: @popover-arrow-outer-width; 64 | } 65 | .popover .arrow:after { 66 | border-width: @popover-arrow-width; 67 | content: ""; 68 | } 69 | 70 | .popover { 71 | &.top .arrow { 72 | left: 50%; 73 | margin-left: -@popover-arrow-outer-width; 74 | border-bottom-width: 0; 75 | border-top-color: @popover-arrow-outer-fallback-color; // IE8 fallback 76 | border-top-color: @popover-arrow-outer-color; 77 | bottom: -@popover-arrow-outer-width; 78 | &:after { 79 | content: " "; 80 | bottom: 1px; 81 | margin-left: -@popover-arrow-width; 82 | border-bottom-width: 0; 83 | border-top-color: @popover-arrow-color; 84 | } 85 | } 86 | &.right .arrow { 87 | top: 50%; 88 | left: -@popover-arrow-outer-width; 89 | margin-top: -@popover-arrow-outer-width; 90 | border-left-width: 0; 91 | border-right-color: @popover-arrow-outer-fallback-color; // IE8 fallback 92 | border-right-color: @popover-arrow-outer-color; 93 | &:after { 94 | content: " "; 95 | left: 1px; 96 | bottom: -@popover-arrow-width; 97 | border-left-width: 0; 98 | border-right-color: @popover-arrow-color; 99 | } 100 | } 101 | &.bottom .arrow { 102 | left: 50%; 103 | margin-left: -@popover-arrow-outer-width; 104 | border-top-width: 0; 105 | border-bottom-color: @popover-arrow-outer-fallback-color; // IE8 fallback 106 | border-bottom-color: @popover-arrow-outer-color; 107 | top: -@popover-arrow-outer-width; 108 | &:after { 109 | content: " "; 110 | top: 1px; 111 | margin-left: -@popover-arrow-width; 112 | border-top-width: 0; 113 | border-bottom-color: @popover-arrow-color; 114 | } 115 | } 116 | 117 | &.left .arrow { 118 | top: 50%; 119 | right: -@popover-arrow-outer-width; 120 | margin-top: -@popover-arrow-outer-width; 121 | border-right-width: 0; 122 | border-left-color: @popover-arrow-outer-fallback-color; // IE8 fallback 123 | border-left-color: @popover-arrow-outer-color; 124 | &:after { 125 | content: " "; 126 | right: 1px; 127 | border-right-width: 0; 128 | border-left-color: @popover-arrow-color; 129 | bottom: -@popover-arrow-width; 130 | } 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/input-groups.less: -------------------------------------------------------------------------------- 1 | // 2 | // Input groups 3 | // -------------------------------------------------- 4 | 5 | // Base styles 6 | // ------------------------- 7 | .input-group { 8 | position: relative; // For dropdowns 9 | display: table; 10 | border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table 11 | 12 | // Undo padding and float of grid classes 13 | &.col { 14 | float: none; 15 | padding-left: 0; 16 | padding-right: 0; 17 | } 18 | 19 | .form-control { 20 | width: 100%; 21 | margin-bottom: 0; 22 | } 23 | } 24 | 25 | // Sizing options 26 | // 27 | // Remix the default form control sizing classes into new ones for easier 28 | // manipulation. 29 | 30 | .input-group-lg > .form-control, 31 | .input-group-lg > .input-group-addon, 32 | .input-group-lg > .input-group-btn > .btn { .input-lg(); } 33 | .input-group-sm > .form-control, 34 | .input-group-sm > .input-group-addon, 35 | .input-group-sm > .input-group-btn > .btn { .input-sm(); } 36 | 37 | 38 | // Display as table-cell 39 | // ------------------------- 40 | .input-group-addon, 41 | .input-group-btn, 42 | .input-group .form-control { 43 | display: table-cell; 44 | 45 | &:not(:first-child):not(:last-child) { 46 | border-radius: 0; 47 | } 48 | } 49 | // Addon and addon wrapper for buttons 50 | .input-group-addon, 51 | .input-group-btn { 52 | width: 1%; 53 | white-space: nowrap; 54 | vertical-align: middle; // Match the inputs 55 | } 56 | 57 | // Text input groups 58 | // ------------------------- 59 | .input-group-addon { 60 | padding: @padding-base-vertical @padding-base-horizontal; 61 | font-size: @font-size-base; 62 | font-weight: normal; 63 | line-height: 1; 64 | color: @input-color; 65 | text-align: center; 66 | background-color: @input-group-addon-bg; 67 | border: 1px solid @input-group-addon-border-color; 68 | border-radius: @border-radius-base; 69 | 70 | // Sizing 71 | &.input-sm { 72 | padding: @padding-small-vertical @padding-small-horizontal; 73 | font-size: @font-size-small; 74 | border-radius: @border-radius-small; 75 | } 76 | &.input-lg { 77 | padding: @padding-large-vertical @padding-large-horizontal; 78 | font-size: @font-size-large; 79 | border-radius: @border-radius-large; 80 | } 81 | 82 | // Nuke default margins from checkboxes and radios to vertically center within. 83 | input[type="radio"], 84 | input[type="checkbox"] { 85 | margin-top: 0; 86 | } 87 | } 88 | 89 | // Reset rounded corners 90 | .input-group .form-control:first-child, 91 | .input-group-addon:first-child, 92 | .input-group-btn:first-child > .btn, 93 | .input-group-btn:first-child > .dropdown-toggle, 94 | .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { 95 | .border-right-radius(0); 96 | } 97 | .input-group-addon:first-child { 98 | border-right: 0; 99 | } 100 | .input-group .form-control:last-child, 101 | .input-group-addon:last-child, 102 | .input-group-btn:last-child > .btn, 103 | .input-group-btn:last-child > .dropdown-toggle, 104 | .input-group-btn:first-child > .btn:not(:first-child) { 105 | .border-left-radius(0); 106 | } 107 | .input-group-addon:last-child { 108 | border-left: 0; 109 | } 110 | 111 | // Button input groups 112 | // ------------------------- 113 | .input-group-btn { 114 | position: relative; 115 | white-space: nowrap; 116 | 117 | // Negative margin to only have a 1px border between the two 118 | &:first-child > .btn { 119 | margin-right: -1px; 120 | } 121 | &:last-child > .btn { 122 | margin-left: -1px; 123 | } 124 | } 125 | .input-group-btn > .btn { 126 | position: relative; 127 | // Jankily prevent input button groups from wrapping 128 | + .btn { 129 | margin-left: -4px; 130 | } 131 | // Bring the "active" button to the front 132 | &:hover, 133 | &:active { 134 | z-index: 2; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/buttons.less: -------------------------------------------------------------------------------- 1 | // 2 | // Buttons 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // -------------------------------------------------- 8 | 9 | // Core styles 10 | .btn { 11 | display: inline-block; 12 | margin-bottom: 0; // For input.btn 13 | font-weight: @btn-font-weight; 14 | text-align: center; 15 | vertical-align: middle; 16 | cursor: pointer; 17 | background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 18 | border: 1px solid transparent; 19 | white-space: nowrap; 20 | .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base); 21 | .user-select(none); 22 | 23 | &:focus { 24 | .tab-focus(); 25 | } 26 | 27 | &:hover, 28 | &:focus { 29 | color: @btn-default-color; 30 | text-decoration: none; 31 | } 32 | 33 | &:active, 34 | &.active { 35 | outline: 0; 36 | background-image: none; 37 | .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); 38 | } 39 | 40 | &.disabled, 41 | &[disabled], 42 | fieldset[disabled] & { 43 | cursor: not-allowed; 44 | pointer-events: none; // Future-proof disabling of clicks 45 | .opacity(.65); 46 | .box-shadow(none); 47 | } 48 | 49 | } 50 | 51 | 52 | // Alternate buttons 53 | // -------------------------------------------------- 54 | 55 | .btn-default { 56 | .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border); 57 | } 58 | .btn-primary { 59 | .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border); 60 | } 61 | // Warning appears as orange 62 | .btn-warning { 63 | .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border); 64 | } 65 | // Danger and error appear as red 66 | .btn-danger { 67 | .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border); 68 | } 69 | // Success appears as green 70 | .btn-success { 71 | .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border); 72 | } 73 | // Info appears as blue-green 74 | .btn-info { 75 | .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border); 76 | } 77 | 78 | 79 | // Link buttons 80 | // ------------------------- 81 | 82 | // Make a button look and behave like a link 83 | .btn-link { 84 | color: @link-color; 85 | font-weight: normal; 86 | cursor: pointer; 87 | border-radius: 0; 88 | 89 | &, 90 | &:active, 91 | &[disabled], 92 | fieldset[disabled] & { 93 | background-color: transparent; 94 | .box-shadow(none); 95 | } 96 | &, 97 | &:hover, 98 | &:focus, 99 | &:active { 100 | border-color: transparent; 101 | } 102 | &:hover, 103 | &:focus { 104 | color: @link-hover-color; 105 | text-decoration: underline; 106 | background-color: transparent; 107 | } 108 | &[disabled], 109 | fieldset[disabled] & { 110 | &:hover, 111 | &:focus { 112 | color: @btn-link-disabled-color; 113 | text-decoration: none; 114 | } 115 | } 116 | } 117 | 118 | 119 | // Button Sizes 120 | // -------------------------------------------------- 121 | 122 | .btn-lg { 123 | // line-height: ensure even-numbered height of button next to large input 124 | .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large); 125 | } 126 | .btn-sm, 127 | .btn-xs { 128 | // line-height: ensure proper height of button next to small input 129 | .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small); 130 | } 131 | .btn-xs { 132 | padding: 1px 5px; 133 | } 134 | 135 | 136 | // Block button 137 | // -------------------------------------------------- 138 | 139 | .btn-block { 140 | display: block; 141 | width: 100%; 142 | padding-left: 0; 143 | padding-right: 0; 144 | } 145 | 146 | // Vertically space out multiple block buttons 147 | .btn-block + .btn-block { 148 | margin-top: 5px; 149 | } 150 | 151 | // Specificity overrides 152 | input[type="submit"], 153 | input[type="reset"], 154 | input[type="button"] { 155 | &.btn-block { 156 | width: 100%; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/panels.less: -------------------------------------------------------------------------------- 1 | // 2 | // Panels 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .panel { 8 | margin-bottom: @line-height-computed; 9 | background-color: @panel-bg; 10 | border: 1px solid transparent; 11 | border-radius: @panel-border-radius; 12 | .box-shadow(0 1px 1px rgba(0,0,0,.05)); 13 | } 14 | 15 | // Panel contents 16 | .panel-body { 17 | padding: 15px; 18 | .clearfix(); 19 | } 20 | 21 | 22 | // List groups in panels 23 | // 24 | // By default, space out list group content from panel headings to account for 25 | // any kind of custom content between the two. 26 | 27 | .panel { 28 | > .list-group { 29 | margin-bottom: 0; 30 | 31 | .list-group-item { 32 | border-width: 1px 0; 33 | 34 | // Remove border radius for top one 35 | &:first-child { 36 | .border-top-radius(0); 37 | } 38 | // But keep it for the last one 39 | &:last-child { 40 | border-bottom: 0; 41 | } 42 | } 43 | } 44 | } 45 | // Collapse space between when there's no additional content. 46 | .panel-heading + .list-group { 47 | .list-group-item:first-child { 48 | border-top-width: 0; 49 | } 50 | } 51 | 52 | 53 | // Tables in panels 54 | // 55 | // Place a non-bordered `.table` within a panel (not within a `.panel-body`) and 56 | // watch it go full width. 57 | 58 | .panel { 59 | > .table, 60 | > .table-responsive { 61 | margin-bottom: 0; 62 | } 63 | > .panel-body + .table, 64 | > .panel-body + .table-responsive { 65 | border-top: 1px solid @table-border-color; 66 | } 67 | > .table-bordered, 68 | > .table-responsive > .table-bordered { 69 | border: 0; 70 | > thead, 71 | > tbody, 72 | > tfoot { 73 | > tr { 74 | > th:first-child, 75 | > td:first-child { 76 | border-left: 0; 77 | } 78 | > th:last-child, 79 | > td:last-child { 80 | border-right: 0; 81 | } 82 | 83 | &:last-child > th, 84 | &:last-child > td { 85 | border-bottom: 0; 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | 93 | // Optional heading 94 | .panel-heading { 95 | padding: 10px 15px; 96 | border-bottom: 1px solid transparent; 97 | .border-top-radius(@panel-border-radius - 1); 98 | 99 | & > .dropdown .dropdown-toggle { 100 | color: inherit; 101 | } 102 | } 103 | 104 | // Within heading, strip any `h*` tag of it's default margins for spacing. 105 | .panel-title { 106 | margin-top: 0; 107 | margin-bottom: 0; 108 | font-size: ceil((@font-size-base * 1.125)); 109 | > a { 110 | color: inherit; 111 | } 112 | } 113 | 114 | // Optional footer (stays gray in every modifier class) 115 | .panel-footer { 116 | padding: 10px 15px; 117 | background-color: @panel-footer-bg; 118 | border-top: 1px solid @panel-inner-border; 119 | .border-bottom-radius(@panel-border-radius - 1); 120 | } 121 | 122 | 123 | // Collapsable panels (aka, accordion) 124 | // 125 | // Wrap a series of panels in `.panel-group` to turn them into an accordion with 126 | // the help of our collapse JavaScript plugin. 127 | 128 | .panel-group { 129 | // Tighten up margin so it's only between panels 130 | .panel { 131 | margin-bottom: 0; 132 | border-radius: @panel-border-radius; 133 | overflow: hidden; // crop contents when collapsed 134 | + .panel { 135 | margin-top: 5px; 136 | } 137 | } 138 | 139 | .panel-heading { 140 | border-bottom: 0; 141 | + .panel-collapse .panel-body { 142 | border-top: 1px solid @panel-inner-border; 143 | } 144 | } 145 | .panel-footer { 146 | border-top: 0; 147 | + .panel-collapse .panel-body { 148 | border-bottom: 1px solid @panel-inner-border; 149 | } 150 | } 151 | } 152 | 153 | 154 | // Contextual variations 155 | .panel-default { 156 | .panel-variant(@panel-default-border; @panel-default-text; @panel-default-heading-bg; @panel-default-border); 157 | } 158 | .panel-primary { 159 | .panel-variant(@panel-primary-border; @panel-primary-text; @panel-primary-heading-bg; @panel-primary-border); 160 | } 161 | .panel-success { 162 | .panel-variant(@panel-success-border; @panel-success-text; @panel-success-heading-bg; @panel-success-border); 163 | } 164 | .panel-warning { 165 | .panel-variant(@panel-warning-border; @panel-warning-text; @panel-warning-heading-bg; @panel-warning-border); 166 | } 167 | .panel-danger { 168 | .panel-variant(@panel-danger-border; @panel-danger-text; @panel-danger-heading-bg; @panel-danger-border); 169 | } 170 | .panel-info { 171 | .panel-variant(@panel-info-border; @panel-info-text; @panel-info-heading-bg; @panel-info-border); 172 | } 173 | -------------------------------------------------------------------------------- /Applications/Statistics/Protocols/Statistic.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Protocols; 15 | /** 16 | * 17 | * struct statisticPortocol 18 | * { 19 | * unsigned char module_name_len; 20 | * unsigned char interface_name_len; 21 | * float cost_time; 22 | * unsigned char success; 23 | * int code; 24 | * unsigned short msg_len; 25 | * unsigned int time; 26 | * char[module_name_len] module_name; 27 | * char[interface_name_len] interface_name; 28 | * char[msg_len] msg; 29 | * } 30 | * 31 | * @author workerman.net 32 | */ 33 | class Statistic 34 | { 35 | /** 36 | * 包头长度 37 | * @var integer 38 | */ 39 | const PACKAGE_FIXED_LENGTH = 17; 40 | 41 | /** 42 | * udp 包最大长度 43 | * @var integer 44 | */ 45 | const MAX_UDP_PACKGE_SIZE = 65507; 46 | 47 | /** 48 | * char类型能保存的最大数值 49 | * @var integer 50 | */ 51 | const MAX_CHAR_VALUE = 255; 52 | 53 | /** 54 | * usigned short 能保存的最大数值 55 | * @var integer 56 | */ 57 | const MAX_UNSIGNED_SHORT_VALUE = 65535; 58 | 59 | /** 60 | * input 61 | * @param string $recv_buffer 62 | */ 63 | public static function input($recv_buffer) 64 | { 65 | if(strlen($recv_buffer) < self::PACKAGE_FIXED_LENGTH) 66 | { 67 | return 0; 68 | } 69 | $data = unpack("Cmodule_name_len/Cinterface_name_len/fcost_time/Csuccess/Ncode/nmsg_len/Ntime", $recv_buffer); 70 | return $data['module_name_len'] + $data['interface_name_len'] + $data['msg_len'] + self::PACKAGE_FIXED_LENGTH; 71 | } 72 | 73 | /** 74 | * 编码 75 | * @param string $module 76 | * @param string $interface 77 | * @param float $cost_time 78 | * @param int $success 79 | * @param int $code 80 | * @param string $msg 81 | * @return string 82 | */ 83 | public static function encode($data) 84 | { 85 | $module = $data['module']; 86 | $interface = $data['interface']; 87 | $cost_time = $data['cost_time']; 88 | $success = $data['success']; 89 | $code = isset($data['code']) ? $data['code'] : 0; 90 | $msg = isset($data['msg']) ? $data['msg'] : ''; 91 | 92 | // 防止模块名过长 93 | if(strlen($module) > self::MAX_CHAR_VALUE) 94 | { 95 | $module = substr($module, 0, self::MAX_CHAR_VALUE); 96 | } 97 | 98 | // 防止接口名过长 99 | if(strlen($interface) > self::MAX_CHAR_VALUE) 100 | { 101 | $interface = substr($interface, 0, self::MAX_CHAR_VALUE); 102 | } 103 | 104 | // 防止msg过长 105 | $module_name_length = strlen($module); 106 | $interface_name_length = strlen($interface); 107 | $avalible_size = self::MAX_UDP_PACKGE_SIZE - self::PACKAGE_FIXED_LENGTH - $module_name_length - $interface_name_length; 108 | if(strlen($msg) > $avalible_size) 109 | { 110 | $msg = substr($msg, 0, $avalible_size); 111 | } 112 | 113 | // 打包 114 | return pack('CCfCNnN', $module_name_length, $interface_name_length, $cost_time, $success ? 1 : 0, $code, strlen($msg), time()).$module.$interface.$msg; 115 | } 116 | 117 | /** 118 | * 解包 119 | * @param string $recv_buffer 120 | * @return array 121 | */ 122 | public static function decode($recv_buffer) 123 | { 124 | // 解包 125 | $data = unpack("Cmodule_name_len/Cinterface_name_len/fcost_time/Csuccess/Ncode/nmsg_len/Ntime", $recv_buffer); 126 | $module = substr($recv_buffer, self::PACKAGE_FIXED_LENGTH, $data['module_name_len']); 127 | $interface = substr($recv_buffer, self::PACKAGE_FIXED_LENGTH + $data['module_name_len'], $data['interface_name_len']); 128 | $msg = substr($recv_buffer, self::PACKAGE_FIXED_LENGTH + $data['module_name_len'] + $data['interface_name_len']); 129 | return array( 130 | 'module' => $module, 131 | 'interface' => $interface, 132 | 'cost_time' => $data['cost_time'], 133 | 'success' => $data['success'], 134 | 'time' => $data['time'], 135 | 'code' => $data['code'], 136 | 'msg' => $msg, 137 | ); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Applications/Statistics/Lib/functions.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | use Workerman\Protocols\Http; 15 | /** 16 | * 批量请求 17 | * @param array $request_buffer_array ['ip:port'=>req_buf, 'ip:port'=>req_buf, ...] 18 | * @return multitype:unknown string 19 | */ 20 | function multiRequest($request_buffer_array) 21 | { 22 | \Statistics\Lib\Cache::$lastSuccessIpArray = array(); 23 | $client_array = $sock_to_ip = $ip_list = array(); 24 | foreach($request_buffer_array as $address => $buffer) 25 | { 26 | list($ip, $port) = explode(':', $address); 27 | $ip_list[$ip] = $ip; 28 | $client = stream_socket_client("tcp://$address", $errno, $errmsg, 1); 29 | if(!$client) 30 | { 31 | continue; 32 | } 33 | $client_array[$address] = $client; 34 | stream_set_timeout($client_array[$address], 0, 100000); 35 | fwrite($client_array[$address], $buffer); 36 | stream_set_blocking($client_array[$address], 0); 37 | $sock_to_address[(int)$client] = $address; 38 | } 39 | $read = $client_array; 40 | $write = $except = $read_buffer = array(); 41 | $time_start = microtime(true); 42 | $timeout = 0.99; 43 | // 轮询处理数据 44 | while(count($read) > 0) 45 | { 46 | if(@stream_select($read, $write, $except, 0, 200000)) 47 | { 48 | foreach($read as $socket) 49 | { 50 | $address = $sock_to_address[(int)$socket]; 51 | $buf = fread($socket, 8192); 52 | if(!$buf) 53 | { 54 | if(feof($socket)) 55 | { 56 | unset($client_array[$address]); 57 | } 58 | continue; 59 | } 60 | if(!isset($read_buffer[$address])) 61 | { 62 | $read_buffer[$address] = $buf; 63 | } 64 | else 65 | { 66 | $read_buffer[$address] .= $buf; 67 | } 68 | // 数据接收完毕 69 | if(($len = strlen($read_buffer[$address])) && $read_buffer[$address][$len-1] === "\n") 70 | { 71 | unset($client_array[$address]); 72 | } 73 | } 74 | } 75 | // 超时了 76 | if(microtime(true) - $time_start > $timeout) 77 | { 78 | break; 79 | } 80 | $read = $client_array; 81 | } 82 | 83 | foreach($read_buffer as $address => $buf) 84 | { 85 | list($ip, $port) = explode(':', $address); 86 | \Statistics\Lib\Cache::$lastSuccessIpArray[$ip] = $ip; 87 | } 88 | 89 | \Statistics\Lib\Cache::$lastFailedIpArray = array_diff($ip_list, \Statistics\Lib\Cache::$lastSuccessIpArray); 90 | 91 | ksort($read_buffer); 92 | 93 | return $read_buffer; 94 | } 95 | 96 | /** 97 | * 检查是否登录 98 | */ 99 | function check_auth() 100 | { 101 | // 如果配置中管理员用户名密码为空则说明不用验证 102 | if(Statistics\Config::$adminName == '' && Statistics\Config::$adminPassword == '') 103 | { 104 | return true; 105 | } 106 | // 进入验证流程 107 | _session_start(); 108 | if(!isset($_SESSION['admin'])) 109 | { 110 | if(!isset($_POST['admin_name']) || !isset($_POST['admin_password'])) 111 | { 112 | include ST_ROOT . '/Views/login.tpl.php'; 113 | _exit(); 114 | } 115 | else 116 | { 117 | $admin_name = $_POST['admin_name']; 118 | $admin_password = $_POST['admin_password']; 119 | if($admin_name != Statistics\Config::$adminName || $admin_password != Statistics\Config::$adminPassword) 120 | { 121 | $msg = "用户名或者密码不正确"; 122 | include ST_ROOT . '/Views/login.tpl.php'; 123 | _exit(); 124 | } 125 | $_SESSION['admin'] = $admin_name; 126 | } 127 | } 128 | return true; 129 | } 130 | 131 | /** 132 | * 启动session,兼容fpm 133 | */ 134 | function _session_start() 135 | { 136 | if(PHP_SAPI === 'cli') 137 | { 138 | return Http::sessionStart(); 139 | } 140 | return session_start(); 141 | } 142 | 143 | /** 144 | * 退出 145 | * @param string $str 146 | */ 147 | function _exit($str = '') 148 | { 149 | if(PHP_SAPI === 'cli') 150 | { 151 | return Http::end($str); 152 | } 153 | return exit($str); 154 | } 155 | -------------------------------------------------------------------------------- /Applications/Statistics/Modules/admin.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Statistics\Modules; 15 | 16 | function admin() 17 | { 18 | $act = isset($_GET['act'])? $_GET['act'] : 'home'; 19 | $err_msg = $notice_msg = $suc_msg = $ip_list_str = ''; 20 | $action = 'save_server_list'; 21 | switch($act) 22 | { 23 | case 'detect_server': 24 | // 创建udp socket 25 | $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); 26 | socket_set_option($socket, SOL_SOCKET, SO_BROADCAST, 1); 27 | $buffer = json_encode(array('cmd'=>'REPORT_IP'))."\n"; 28 | // 广播 29 | socket_sendto($socket, $buffer, strlen($buffer), 0, '255.255.255.255', \Statistics\Config::$ProviderPort); 30 | // 超时相关 31 | $time_start = microtime(true); 32 | $global_timeout = 1; 33 | $ip_list = array(); 34 | $recv_timeout = array('sec'=>0,'usec'=>8000); 35 | socket_set_option($socket,SOL_SOCKET,SO_RCVTIMEO,$recv_timeout); 36 | 37 | // 循环读数据 38 | while(microtime(true) - $time_start < $global_timeout) 39 | { 40 | $buf = $host = $port = ''; 41 | if(@socket_recvfrom($socket, $buf, 65535, 0, $host, $port)) 42 | { 43 | $ip_list[$host] = $host; 44 | } 45 | } 46 | 47 | // 过滤掉已经保存的ip 48 | $count = 0; 49 | foreach($ip_list as $ip) 50 | { 51 | if(!isset(\Statistics\Lib\Cache::$ServerIpList[$ip])) 52 | { 53 | $ip_list_str .= $ip."\r\n"; 54 | $count ++; 55 | } 56 | } 57 | $action = 'add_to_server_list'; 58 | $notice_msg = "探测到{$count}个新数据源"; 59 | break; 60 | case 'add_to_server_list': 61 | if(empty($_POST['ip_list'])) 62 | { 63 | $err_msg = "保存的ip列表为空"; 64 | break; 65 | } 66 | $ip_list = explode("\n", $_POST['ip_list']); 67 | if($ip_list) 68 | { 69 | foreach($ip_list as $ip) 70 | { 71 | $ip = trim($ip); 72 | if(false !== ip2long($ip)) 73 | { 74 | \Statistics\Lib\Cache::$ServerIpList[$ip] = $ip; 75 | } 76 | } 77 | } 78 | $suc_msg = "添加成功"; 79 | foreach(\Statistics\Lib\Cache::$ServerIpList as $ip) 80 | { 81 | $ip_list_str .= $ip."\r\n"; 82 | } 83 | saveServerIpListToCache(); 84 | break; 85 | case 'save_server_list': 86 | if(empty($_POST['ip_list'])) 87 | { 88 | $err_msg = "保存的ip列表为空"; 89 | break; 90 | } 91 | \Statistics\Lib\Cache::$ServerIpList = array(); 92 | $ip_list = explode("\n", $_POST['ip_list']); 93 | if($ip_list) 94 | { 95 | foreach($ip_list as $ip) 96 | { 97 | $ip = trim($ip); 98 | if(false !== ip2long($ip)) 99 | { 100 | \Statistics\Lib\Cache::$ServerIpList[$ip] = $ip; 101 | } 102 | } 103 | } 104 | $suc_msg = "保存成功"; 105 | foreach(\Statistics\Lib\Cache::$ServerIpList as $ip) 106 | { 107 | $ip_list_str .= $ip."\r\n"; 108 | } 109 | saveServerIpListToCache(); 110 | break; 111 | default: 112 | foreach(\Statistics\Lib\Cache::$ServerIpList as $ip) 113 | { 114 | $ip_list_str .= $ip."\r\n"; 115 | } 116 | } 117 | 118 | include ST_ROOT . '/Views/header.tpl.php'; 119 | include ST_ROOT . '/Views/admin.tpl.php'; 120 | include ST_ROOT . '/Views/footer.tpl.php'; 121 | } 122 | 123 | function saveServerIpListToCache() 124 | { 125 | foreach(glob(ST_ROOT . '/Config/Cache/*.iplist.cache.php') as $php_file) 126 | { 127 | unlink($php_file); 128 | } 129 | file_put_contents(ST_ROOT . '/Config/Cache/'.time().'.iplist.cache.php', " li > a { 64 | display: block; 65 | padding: 3px 20px; 66 | clear: both; 67 | font-weight: normal; 68 | line-height: @line-height-base; 69 | color: @dropdown-link-color; 70 | white-space: nowrap; // prevent links from randomly breaking onto new lines 71 | } 72 | } 73 | 74 | // Hover/Focus state 75 | .dropdown-menu > li > a { 76 | &:hover, 77 | &:focus { 78 | text-decoration: none; 79 | color: @dropdown-link-hover-color; 80 | background-color: @dropdown-link-hover-bg; 81 | } 82 | } 83 | 84 | // Active state 85 | .dropdown-menu > .active > a { 86 | &, 87 | &:hover, 88 | &:focus { 89 | color: @dropdown-link-active-color; 90 | text-decoration: none; 91 | outline: 0; 92 | background-color: @dropdown-link-active-bg; 93 | } 94 | } 95 | 96 | // Disabled state 97 | // 98 | // Gray out text and ensure the hover/focus state remains gray 99 | 100 | .dropdown-menu > .disabled > a { 101 | &, 102 | &:hover, 103 | &:focus { 104 | color: @dropdown-link-disabled-color; 105 | } 106 | } 107 | // Nuke hover/focus effects 108 | .dropdown-menu > .disabled > a { 109 | &:hover, 110 | &:focus { 111 | text-decoration: none; 112 | background-color: transparent; 113 | background-image: none; // Remove CSS gradient 114 | .reset-filter(); 115 | cursor: not-allowed; 116 | } 117 | } 118 | 119 | // Open state for the dropdown 120 | .open { 121 | // Show the menu 122 | > .dropdown-menu { 123 | display: block; 124 | } 125 | 126 | // Remove the outline when :focus is triggered 127 | > a { 128 | outline: 0; 129 | } 130 | } 131 | 132 | // Dropdown section headers 133 | .dropdown-header { 134 | display: block; 135 | padding: 3px 20px; 136 | font-size: @font-size-small; 137 | line-height: @line-height-base; 138 | color: @dropdown-header-color; 139 | } 140 | 141 | // Backdrop to catch body clicks on mobile, etc. 142 | .dropdown-backdrop { 143 | position: fixed; 144 | left: 0; 145 | right: 0; 146 | bottom: 0; 147 | top: 0; 148 | z-index: @zindex-dropdown - 10; 149 | } 150 | 151 | // Right aligned dropdowns 152 | .pull-right > .dropdown-menu { 153 | right: 0; 154 | left: auto; 155 | } 156 | 157 | // Allow for dropdowns to go bottom up (aka, dropup-menu) 158 | // 159 | // Just add .dropup after the standard .dropdown class and you're set, bro. 160 | // TODO: abstract this so that the navbar fixed styles are not placed here? 161 | 162 | .dropup, 163 | .navbar-fixed-bottom .dropdown { 164 | // Reverse the caret 165 | .caret { 166 | // Firefox fix for https://github.com/twbs/bootstrap/issues/9538. Once this 167 | // gets fixed, restore `border-top: 0;`. 168 | border-top: 0 dotted; 169 | border-bottom: @caret-width-base solid @dropdown-caret-color; 170 | content: ""; 171 | } 172 | // Different positioning for bottom up menu 173 | .dropdown-menu { 174 | top: auto; 175 | bottom: 100%; 176 | margin-bottom: 1px; 177 | } 178 | } 179 | 180 | 181 | // Component alignment 182 | // 183 | // Reiterate per navbar.less and the modified component alignment there. 184 | 185 | @media (min-width: @grid-float-breakpoint) { 186 | .navbar-right { 187 | .dropdown-menu { 188 | .pull-right > .dropdown-menu(); 189 | } 190 | } 191 | } 192 | 193 | -------------------------------------------------------------------------------- /Applications/Statistics/Views/statistic.tpl.php: -------------------------------------------------------------------------------- 1 |
      2 |
      3 |
      4 | 32 |
      33 |
      34 |
      35 |
      36 |
      37 |
      38 |
      39 | 40 |
      41 | 42 | 43 |
      44 | 45 | 46 |
      47 |
      48 | 49 |
      50 |
      51 |
      52 |
      53 |
      54 |
      55 |
      56 |
      57 |
      58 |
      59 | 60 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 |
      时间调用总数平均耗时成功调用总数成功平均耗时失败调用总数失败平均耗时成功率
      193 | 194 |
      195 |
      196 |
      197 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/tables.less: -------------------------------------------------------------------------------- 1 | // 2 | // Tables 3 | // -------------------------------------------------- 4 | 5 | 6 | table { 7 | max-width: 100%; 8 | background-color: @table-bg; 9 | } 10 | th { 11 | text-align: left; 12 | } 13 | 14 | 15 | // Baseline styles 16 | 17 | .table { 18 | width: 100%; 19 | margin-bottom: @line-height-computed; 20 | // Cells 21 | > thead, 22 | > tbody, 23 | > tfoot { 24 | > tr { 25 | > th, 26 | > td { 27 | padding: @table-cell-padding; 28 | line-height: @line-height-base; 29 | vertical-align: top; 30 | border-top: 1px solid @table-border-color; 31 | } 32 | } 33 | } 34 | // Bottom align for column headings 35 | > thead > tr > th { 36 | vertical-align: bottom; 37 | border-bottom: 2px solid @table-border-color; 38 | } 39 | // Remove top border from thead by default 40 | > caption + thead, 41 | > colgroup + thead, 42 | > thead:first-child { 43 | > tr:first-child { 44 | > th, 45 | > td { 46 | border-top: 0; 47 | } 48 | } 49 | } 50 | // Account for multiple tbody instances 51 | > tbody + tbody { 52 | border-top: 2px solid @table-border-color; 53 | } 54 | 55 | // Nesting 56 | .table { 57 | background-color: @body-bg; 58 | } 59 | } 60 | 61 | 62 | // Condensed table w/ half padding 63 | 64 | .table-condensed { 65 | > thead, 66 | > tbody, 67 | > tfoot { 68 | > tr { 69 | > th, 70 | > td { 71 | padding: @table-condensed-cell-padding; 72 | } 73 | } 74 | } 75 | } 76 | 77 | 78 | // Bordered version 79 | // 80 | // Add borders all around the table and between all the columns. 81 | 82 | .table-bordered { 83 | border: 1px solid @table-border-color; 84 | > thead, 85 | > tbody, 86 | > tfoot { 87 | > tr { 88 | > th, 89 | > td { 90 | border: 1px solid @table-border-color; 91 | } 92 | } 93 | } 94 | > thead > tr { 95 | > th, 96 | > td { 97 | border-bottom-width: 2px; 98 | } 99 | } 100 | } 101 | 102 | 103 | // Zebra-striping 104 | // 105 | // Default zebra-stripe styles (alternating gray and transparent backgrounds) 106 | 107 | .table-striped > tbody > tr:nth-child(odd) { 108 | > td, 109 | > th { 110 | background-color: @table-bg-accent; 111 | } 112 | } 113 | 114 | 115 | // Hover effect 116 | // 117 | // Placed here since it has to come after the potential zebra striping 118 | 119 | .table-hover > tbody > tr:hover { 120 | > td, 121 | > th { 122 | background-color: @table-bg-hover; 123 | } 124 | } 125 | 126 | 127 | // Table cell sizing 128 | // 129 | // Reset default table behavior 130 | 131 | table col[class*="col-"] { 132 | float: none; 133 | display: table-column; 134 | } 135 | table { 136 | td, 137 | th { 138 | &[class*="col-"] { 139 | float: none; 140 | display: table-cell; 141 | } 142 | } 143 | } 144 | 145 | 146 | // Table backgrounds 147 | // 148 | // Exact selectors below required to override `.table-striped` and prevent 149 | // inheritance to nested tables. 150 | 151 | .table > thead > tr, 152 | .table > tbody > tr, 153 | .table > tfoot > tr { 154 | > td.active, 155 | > th.active, 156 | &.active > td, 157 | &.active > th { 158 | background-color: @table-bg-active; 159 | } 160 | } 161 | 162 | // Generate the contextual variants 163 | .table-row-variant(success; @state-success-bg; @state-success-border); 164 | .table-row-variant(danger; @state-danger-bg; @state-danger-border); 165 | .table-row-variant(warning; @state-warning-bg; @state-warning-border); 166 | 167 | 168 | // Responsive tables 169 | // 170 | // Wrap your tables in `.table-responsive` and we'll make them mobile friendly 171 | // by enabling horizontal scrolling. Only applies <768px. Everything above that 172 | // will display normally. 173 | 174 | @media (max-width: @screen-xs-max) { 175 | .table-responsive { 176 | width: 100%; 177 | margin-bottom: (@line-height-computed * 0.75); 178 | overflow-y: hidden; 179 | overflow-x: scroll; 180 | -ms-overflow-style: -ms-autohiding-scrollbar; 181 | border: 1px solid @table-border-color; 182 | -webkit-overflow-scrolling: touch; 183 | 184 | // Tighten up spacing 185 | > .table { 186 | margin-bottom: 0; 187 | 188 | // Ensure the content doesn't wrap 189 | > thead, 190 | > tbody, 191 | > tfoot { 192 | > tr { 193 | > th, 194 | > td { 195 | white-space: nowrap; 196 | } 197 | } 198 | } 199 | } 200 | 201 | // Special overrides for the bordered tables 202 | > .table-bordered { 203 | border: 0; 204 | 205 | // Nuke the appropriate borders so that the parent can handle them 206 | > thead, 207 | > tbody, 208 | > tfoot { 209 | > tr { 210 | > th:first-child, 211 | > td:first-child { 212 | border-left: 0; 213 | } 214 | > th:last-child, 215 | > td:last-child { 216 | border-right: 0; 217 | } 218 | } 219 | } 220 | 221 | // Only nuke the last row's bottom-border in `tbody` and `tfoot` since 222 | // chances are there will be only one `tr` in a `thead` and that would 223 | // remove the border altogether. 224 | > tbody, 225 | > tfoot { 226 | > tr:last-child { 227 | > th, 228 | > td { 229 | border-bottom: 0; 230 | } 231 | } 232 | } 233 | 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/carousel.less: -------------------------------------------------------------------------------- 1 | // 2 | // Carousel 3 | // -------------------------------------------------- 4 | 5 | 6 | // Wrapper for the slide container and indicators 7 | .carousel { 8 | position: relative; 9 | } 10 | 11 | .carousel-inner { 12 | position: relative; 13 | overflow: hidden; 14 | width: 100%; 15 | 16 | > .item { 17 | display: none; 18 | position: relative; 19 | .transition(.6s ease-in-out left); 20 | 21 | // Account for jankitude on images 22 | > img, 23 | > a > img { 24 | .img-responsive(); 25 | line-height: 1; 26 | } 27 | } 28 | 29 | > .active, 30 | > .next, 31 | > .prev { display: block; } 32 | 33 | > .active { 34 | left: 0; 35 | } 36 | 37 | > .next, 38 | > .prev { 39 | position: absolute; 40 | top: 0; 41 | width: 100%; 42 | } 43 | 44 | > .next { 45 | left: 100%; 46 | } 47 | > .prev { 48 | left: -100%; 49 | } 50 | > .next.left, 51 | > .prev.right { 52 | left: 0; 53 | } 54 | 55 | > .active.left { 56 | left: -100%; 57 | } 58 | > .active.right { 59 | left: 100%; 60 | } 61 | 62 | } 63 | 64 | // Left/right controls for nav 65 | // --------------------------- 66 | 67 | .carousel-control { 68 | position: absolute; 69 | top: 0; 70 | left: 0; 71 | bottom: 0; 72 | width: @carousel-control-width; 73 | .opacity(@carousel-control-opacity); 74 | font-size: @carousel-control-font-size; 75 | color: @carousel-control-color; 76 | text-align: center; 77 | text-shadow: @carousel-text-shadow; 78 | // We can't have this transition here because WebKit cancels the carousel 79 | // animation if you trip this while in the middle of another animation. 80 | 81 | // Set gradients for backgrounds 82 | &.left { 83 | #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001)); 84 | } 85 | &.right { 86 | left: auto; 87 | right: 0; 88 | #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5)); 89 | } 90 | 91 | // Hover/focus state 92 | &:hover, 93 | &:focus { 94 | color: @carousel-control-color; 95 | text-decoration: none; 96 | .opacity(.9); 97 | } 98 | 99 | // Toggles 100 | .icon-prev, 101 | .icon-next, 102 | .glyphicon-chevron-left, 103 | .glyphicon-chevron-right { 104 | position: absolute; 105 | top: 50%; 106 | z-index: 5; 107 | display: inline-block; 108 | } 109 | .icon-prev, 110 | .glyphicon-chevron-left { 111 | left: 50%; 112 | } 113 | .icon-next, 114 | .glyphicon-chevron-right { 115 | right: 50%; 116 | } 117 | .icon-prev, 118 | .icon-next { 119 | width: 20px; 120 | height: 20px; 121 | margin-top: -10px; 122 | margin-left: -10px; 123 | font-family: serif; 124 | } 125 | 126 | .icon-prev { 127 | &:before { 128 | content: '\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039) 129 | } 130 | } 131 | .icon-next { 132 | &:before { 133 | content: '\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A) 134 | } 135 | } 136 | } 137 | 138 | // Optional indicator pips 139 | // 140 | // Add an unordered list with the following class and add a list item for each 141 | // slide your carousel holds. 142 | 143 | .carousel-indicators { 144 | position: absolute; 145 | bottom: 10px; 146 | left: 50%; 147 | z-index: 15; 148 | width: 60%; 149 | margin-left: -30%; 150 | padding-left: 0; 151 | list-style: none; 152 | text-align: center; 153 | 154 | li { 155 | display: inline-block; 156 | width: 10px; 157 | height: 10px; 158 | margin: 1px; 159 | text-indent: -999px; 160 | border: 1px solid @carousel-indicator-border-color; 161 | border-radius: 10px; 162 | cursor: pointer; 163 | 164 | // IE8-9 hack for event handling 165 | // 166 | // Internet Explorer 8-9 does not support clicks on elements without a set 167 | // `background-color`. We cannot use `filter` since that's not viewed as a 168 | // background color by the browser. Thus, a hack is needed. 169 | // 170 | // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we 171 | // set alpha transparency for the best results possible. 172 | background-color: #000 \9; // IE8 173 | background-color: rgba(0,0,0,0); // IE9 174 | } 175 | .active { 176 | margin: 0; 177 | width: 12px; 178 | height: 12px; 179 | background-color: @carousel-indicator-active-bg; 180 | } 181 | } 182 | 183 | // Optional captions 184 | // ----------------------------- 185 | // Hidden by default for smaller viewports 186 | .carousel-caption { 187 | position: absolute; 188 | left: 15%; 189 | right: 15%; 190 | bottom: 20px; 191 | z-index: 10; 192 | padding-top: 20px; 193 | padding-bottom: 20px; 194 | color: @carousel-caption-color; 195 | text-align: center; 196 | text-shadow: @carousel-text-shadow; 197 | & .btn { 198 | text-shadow: none; // No shadow for button elements in carousel-caption 199 | } 200 | } 201 | 202 | 203 | // Scale up controls for tablets and up 204 | @media screen and (min-width: @screen-sm-min) { 205 | 206 | // Scale up the controls a smidge 207 | .carousel-control { 208 | .glyphicons-chevron-left, 209 | .glyphicons-chevron-right, 210 | .icon-prev, 211 | .icon-next { 212 | width: 30px; 213 | height: 30px; 214 | margin-top: -15px; 215 | margin-left: -15px; 216 | font-size: 30px; 217 | } 218 | } 219 | 220 | // Show and left align the captions 221 | .carousel-caption { 222 | left: 20%; 223 | right: 20%; 224 | padding-bottom: 30px; 225 | } 226 | 227 | // Move up the indicators 228 | .carousel-indicators { 229 | bottom: 20px; 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/responsive-utilities.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // IE10 in Windows (Phone) 8 7 | // 8 | // Support for responsive views via media queries is kind of borked in IE10, for 9 | // Surface/desktop in split view and for Windows Phone 8. This particular fix 10 | // must be accompanied by a snippet of JavaScript to sniff the user agent and 11 | // apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at 12 | // our Getting Started page for more information on this bug. 13 | // 14 | // For more information, see the following: 15 | // 16 | // Issue: https://github.com/twbs/bootstrap/issues/10497 17 | // Docs: http://getbootstrap.com/getting-started/#browsers 18 | // Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/ 19 | 20 | @-ms-viewport { 21 | width: device-width; 22 | } 23 | 24 | 25 | // Visibility utilities 26 | 27 | .visible-xs { 28 | .responsive-invisibility(); 29 | @media (max-width: @screen-xs-max) { 30 | .responsive-visibility(); 31 | } 32 | &.visible-sm { 33 | @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { 34 | .responsive-visibility(); 35 | } 36 | } 37 | &.visible-md { 38 | @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { 39 | .responsive-visibility(); 40 | } 41 | } 42 | &.visible-lg { 43 | @media (min-width: @screen-lg-min) { 44 | .responsive-visibility(); 45 | } 46 | } 47 | } 48 | .visible-sm { 49 | .responsive-invisibility(); 50 | &.visible-xs { 51 | @media (max-width: @screen-xs-max) { 52 | .responsive-visibility(); 53 | } 54 | } 55 | @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { 56 | .responsive-visibility(); 57 | } 58 | &.visible-md { 59 | @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { 60 | .responsive-visibility(); 61 | } 62 | } 63 | &.visible-lg { 64 | @media (min-width: @screen-lg-min) { 65 | .responsive-visibility(); 66 | } 67 | } 68 | } 69 | .visible-md { 70 | .responsive-invisibility(); 71 | &.visible-xs { 72 | @media (max-width: @screen-xs-max) { 73 | .responsive-visibility(); 74 | } 75 | } 76 | &.visible-sm { 77 | @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { 78 | .responsive-visibility(); 79 | } 80 | } 81 | @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { 82 | .responsive-visibility(); 83 | } 84 | &.visible-lg { 85 | @media (min-width: @screen-lg-min) { 86 | .responsive-visibility(); 87 | } 88 | } 89 | } 90 | .visible-lg { 91 | .responsive-invisibility(); 92 | &.visible-xs { 93 | @media (max-width: @screen-xs-max) { 94 | .responsive-visibility(); 95 | } 96 | } 97 | &.visible-sm { 98 | @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { 99 | .responsive-visibility(); 100 | } 101 | } 102 | &.visible-md { 103 | @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { 104 | .responsive-visibility(); 105 | } 106 | } 107 | @media (min-width: @screen-lg-min) { 108 | .responsive-visibility(); 109 | } 110 | } 111 | 112 | .hidden-xs { 113 | .responsive-visibility(); 114 | @media (max-width: @screen-xs-max) { 115 | .responsive-invisibility(); 116 | } 117 | &.hidden-sm { 118 | @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { 119 | .responsive-invisibility(); 120 | } 121 | } 122 | &.hidden-md { 123 | @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { 124 | .responsive-invisibility(); 125 | } 126 | } 127 | &.hidden-lg { 128 | @media (min-width: @screen-lg-min) { 129 | .responsive-invisibility(); 130 | } 131 | } 132 | } 133 | .hidden-sm { 134 | .responsive-visibility(); 135 | &.hidden-xs { 136 | @media (max-width: @screen-xs-max) { 137 | .responsive-invisibility(); 138 | } 139 | } 140 | @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { 141 | .responsive-invisibility(); 142 | } 143 | &.hidden-md { 144 | @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { 145 | .responsive-invisibility(); 146 | } 147 | } 148 | &.hidden-lg { 149 | @media (min-width: @screen-lg-min) { 150 | .responsive-invisibility(); 151 | } 152 | } 153 | } 154 | .hidden-md { 155 | .responsive-visibility(); 156 | &.hidden-xs { 157 | @media (max-width: @screen-xs-max) { 158 | .responsive-invisibility(); 159 | } 160 | } 161 | &.hidden-sm { 162 | @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { 163 | .responsive-invisibility(); 164 | } 165 | } 166 | @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { 167 | .responsive-invisibility(); 168 | } 169 | &.hidden-lg { 170 | @media (min-width: @screen-lg-min) { 171 | .responsive-invisibility(); 172 | } 173 | } 174 | } 175 | .hidden-lg { 176 | .responsive-visibility(); 177 | &.hidden-xs { 178 | @media (max-width: @screen-xs-max) { 179 | .responsive-invisibility(); 180 | } 181 | } 182 | &.hidden-sm { 183 | @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { 184 | .responsive-invisibility(); 185 | } 186 | } 187 | &.hidden-md { 188 | @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { 189 | .responsive-invisibility(); 190 | } 191 | } 192 | @media (min-width: @screen-lg-min) { 193 | .responsive-invisibility(); 194 | } 195 | } 196 | 197 | // Print utilities 198 | .visible-print { 199 | .responsive-invisibility(); 200 | } 201 | 202 | @media print { 203 | .visible-print { 204 | .responsive-visibility(); 205 | } 206 | .hidden-print { 207 | .responsive-invisibility(); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/type.less: -------------------------------------------------------------------------------- 1 | // 2 | // Typography 3 | // -------------------------------------------------- 4 | 5 | 6 | // Body text 7 | // ------------------------- 8 | 9 | p { 10 | margin: 0 0 (@line-height-computed / 2); 11 | } 12 | .lead { 13 | margin-bottom: @line-height-computed; 14 | font-size: floor(@font-size-base * 1.15); 15 | font-weight: 200; 16 | line-height: 1.4; 17 | 18 | @media (min-width: @screen-sm-min) { 19 | font-size: (@font-size-base * 1.5); 20 | } 21 | } 22 | 23 | 24 | // Emphasis & misc 25 | // ------------------------- 26 | 27 | // Ex: 14px base font * 85% = about 12px 28 | small, 29 | .small { font-size: 85%; } 30 | 31 | // Undo browser default styling 32 | cite { font-style: normal; } 33 | 34 | // Contextual emphasis 35 | .text-muted { 36 | color: @text-muted; 37 | } 38 | .text-primary { 39 | color: @brand-primary; 40 | &:hover { 41 | color: darken(@brand-primary, 10%); 42 | } 43 | } 44 | .text-warning { 45 | color: @state-warning-text; 46 | &:hover { 47 | color: darken(@state-warning-text, 10%); 48 | } 49 | } 50 | .text-danger { 51 | color: @state-danger-text; 52 | &:hover { 53 | color: darken(@state-danger-text, 10%); 54 | } 55 | } 56 | .text-success { 57 | color: @state-success-text; 58 | &:hover { 59 | color: darken(@state-success-text, 10%); 60 | } 61 | } 62 | .text-info { 63 | color: @state-info-text; 64 | &:hover { 65 | color: darken(@state-info-text, 10%); 66 | } 67 | } 68 | 69 | // Alignment 70 | .text-left { text-align: left; } 71 | .text-right { text-align: right; } 72 | .text-center { text-align: center; } 73 | 74 | 75 | // Headings 76 | // ------------------------- 77 | 78 | h1, h2, h3, h4, h5, h6, 79 | .h1, .h2, .h3, .h4, .h5, .h6 { 80 | font-family: @headings-font-family; 81 | font-weight: @headings-font-weight; 82 | line-height: @headings-line-height; 83 | color: @headings-color; 84 | 85 | small, 86 | .small { 87 | font-weight: normal; 88 | line-height: 1; 89 | color: @headings-small-color; 90 | } 91 | } 92 | 93 | h1, 94 | h2, 95 | h3 { 96 | margin-top: @line-height-computed; 97 | margin-bottom: (@line-height-computed / 2); 98 | 99 | small, 100 | .small { 101 | font-size: 65%; 102 | } 103 | } 104 | h4, 105 | h5, 106 | h6 { 107 | margin-top: (@line-height-computed / 2); 108 | margin-bottom: (@line-height-computed / 2); 109 | 110 | small, 111 | .small { 112 | font-size: 75%; 113 | } 114 | } 115 | 116 | h1, .h1 { font-size: @font-size-h1; } 117 | h2, .h2 { font-size: @font-size-h2; } 118 | h3, .h3 { font-size: @font-size-h3; } 119 | h4, .h4 { font-size: @font-size-h4; } 120 | h5, .h5 { font-size: @font-size-h5; } 121 | h6, .h6 { font-size: @font-size-h6; } 122 | 123 | 124 | // Page header 125 | // ------------------------- 126 | 127 | .page-header { 128 | padding-bottom: ((@line-height-computed / 2) - 1); 129 | margin: (@line-height-computed * 2) 0 @line-height-computed; 130 | border-bottom: 1px solid @page-header-border-color; 131 | } 132 | 133 | 134 | 135 | // Lists 136 | // -------------------------------------------------- 137 | 138 | // Unordered and Ordered lists 139 | ul, 140 | ol { 141 | margin-top: 0; 142 | margin-bottom: (@line-height-computed / 2); 143 | ul, 144 | ol { 145 | margin-bottom: 0; 146 | } 147 | } 148 | 149 | // List options 150 | 151 | // Unstyled keeps list items block level, just removes default browser padding and list-style 152 | .list-unstyled { 153 | padding-left: 0; 154 | list-style: none; 155 | } 156 | 157 | // Inline turns list items into inline-block 158 | .list-inline { 159 | .list-unstyled(); 160 | 161 | > li { 162 | display: inline-block; 163 | padding-left: 5px; 164 | padding-right: 5px; 165 | 166 | &:first-child { 167 | padding-left: 0; 168 | } 169 | } 170 | } 171 | 172 | // Description Lists 173 | dl { 174 | margin-bottom: @line-height-computed; 175 | } 176 | dt, 177 | dd { 178 | line-height: @line-height-base; 179 | } 180 | dt { 181 | font-weight: bold; 182 | } 183 | dd { 184 | margin-left: 0; // Undo browser default 185 | } 186 | 187 | // Horizontal description lists 188 | // 189 | // Defaults to being stacked without any of the below styles applied, until the 190 | // grid breakpoint is reached (default of ~768px). 191 | 192 | @media (min-width: @grid-float-breakpoint) { 193 | .dl-horizontal { 194 | dt { 195 | float: left; 196 | width: (@component-offset-horizontal - 20); 197 | clear: left; 198 | text-align: right; 199 | .text-overflow(); 200 | } 201 | dd { 202 | margin-left: @component-offset-horizontal; 203 | .clearfix(); // Clear the floated `dt` if an empty `dd` is present 204 | } 205 | } 206 | } 207 | 208 | // MISC 209 | // ---- 210 | 211 | // Abbreviations and acronyms 212 | abbr[title], 213 | // Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257 214 | abbr[data-original-title] { 215 | cursor: help; 216 | border-bottom: 1px dotted @abbr-border-color; 217 | } 218 | abbr.initialism { 219 | font-size: 90%; 220 | text-transform: uppercase; 221 | } 222 | 223 | // Blockquotes 224 | blockquote { 225 | padding: (@line-height-computed / 2) @line-height-computed; 226 | margin: 0 0 @line-height-computed; 227 | border-left: 5px solid @blockquote-border-color; 228 | p { 229 | font-size: (@font-size-base * 1.25); 230 | font-weight: 300; 231 | line-height: 1.25; 232 | } 233 | p:last-child { 234 | margin-bottom: 0; 235 | } 236 | small { 237 | display: block; 238 | line-height: @line-height-base; 239 | color: @blockquote-small-color; 240 | &:before { 241 | content: '\2014 \00A0'; // EM DASH, NBSP 242 | } 243 | } 244 | 245 | // Float right with text-align: right 246 | &.pull-right { 247 | padding-right: 15px; 248 | padding-left: 0; 249 | border-right: 5px solid @blockquote-border-color; 250 | border-left: 0; 251 | p, 252 | small, 253 | .small { 254 | text-align: right; 255 | } 256 | small, 257 | .small { 258 | &:before { 259 | content: ''; 260 | } 261 | &:after { 262 | content: '\00A0 \2014'; // NBSP, EM DASH 263 | } 264 | } 265 | } 266 | } 267 | 268 | // Quotes 269 | blockquote:before, 270 | blockquote:after { 271 | content: ""; 272 | } 273 | 274 | // Addresses 275 | address { 276 | margin-bottom: @line-height-computed; 277 | font-style: normal; 278 | line-height: @line-height-base; 279 | } 280 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/button-groups.less: -------------------------------------------------------------------------------- 1 | // 2 | // Button groups 3 | // -------------------------------------------------- 4 | 5 | // Button carets 6 | // 7 | // Match the button text color to the arrow/caret for indicating dropdown-ness. 8 | 9 | .caret { 10 | .btn-default & { 11 | border-top-color: @btn-default-color; 12 | } 13 | .btn-primary &, 14 | .btn-success &, 15 | .btn-warning &, 16 | .btn-danger &, 17 | .btn-info & { 18 | border-top-color: #fff; 19 | } 20 | } 21 | .dropup { 22 | .btn-default .caret { 23 | border-bottom-color: @btn-default-color; 24 | } 25 | .btn-primary, 26 | .btn-success, 27 | .btn-warning, 28 | .btn-danger, 29 | .btn-info { 30 | .caret { 31 | border-bottom-color: #fff; 32 | } 33 | } 34 | } 35 | 36 | // Make the div behave like a button 37 | .btn-group, 38 | .btn-group-vertical { 39 | position: relative; 40 | display: inline-block; 41 | vertical-align: middle; // match .btn alignment given font-size hack above 42 | > .btn { 43 | position: relative; 44 | float: left; 45 | // Bring the "active" button to the front 46 | &:hover, 47 | &:focus, 48 | &:active, 49 | &.active { 50 | z-index: 2; 51 | } 52 | &:focus { 53 | // Remove focus outline when dropdown JS adds it after closing the menu 54 | outline: none; 55 | } 56 | } 57 | } 58 | 59 | // Prevent double borders when buttons are next to each other 60 | .btn-group { 61 | .btn + .btn, 62 | .btn + .btn-group, 63 | .btn-group + .btn, 64 | .btn-group + .btn-group { 65 | margin-left: -1px; 66 | } 67 | } 68 | 69 | // Optional: Group multiple button groups together for a toolbar 70 | .btn-toolbar { 71 | .clearfix(); 72 | 73 | .btn-group { 74 | float: left; 75 | } 76 | // Space out series of button groups 77 | > .btn, 78 | > .btn-group { 79 | + .btn, 80 | + .btn-group { 81 | margin-left: 5px; 82 | } 83 | } 84 | } 85 | 86 | .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { 87 | border-radius: 0; 88 | } 89 | 90 | // Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match 91 | .btn-group > .btn:first-child { 92 | margin-left: 0; 93 | &:not(:last-child):not(.dropdown-toggle) { 94 | .border-right-radius(0); 95 | } 96 | } 97 | // Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it 98 | .btn-group > .btn:last-child:not(:first-child), 99 | .btn-group > .dropdown-toggle:not(:first-child) { 100 | .border-left-radius(0); 101 | } 102 | 103 | // Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) 104 | .btn-group > .btn-group { 105 | float: left; 106 | } 107 | .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { 108 | border-radius: 0; 109 | } 110 | .btn-group > .btn-group:first-child { 111 | > .btn:last-child, 112 | > .dropdown-toggle { 113 | .border-right-radius(0); 114 | } 115 | } 116 | .btn-group > .btn-group:last-child > .btn:first-child { 117 | .border-left-radius(0); 118 | } 119 | 120 | // On active and open, don't show outline 121 | .btn-group .dropdown-toggle:active, 122 | .btn-group.open .dropdown-toggle { 123 | outline: 0; 124 | } 125 | 126 | 127 | // Sizing 128 | // 129 | // Remix the default button sizing classes into new ones for easier manipulation. 130 | 131 | .btn-group-xs > .btn { .btn-xs(); } 132 | .btn-group-sm > .btn { .btn-sm(); } 133 | .btn-group-lg > .btn { .btn-lg(); } 134 | 135 | 136 | // Split button dropdowns 137 | // ---------------------- 138 | 139 | // Give the line between buttons some depth 140 | .btn-group > .btn + .dropdown-toggle { 141 | padding-left: 8px; 142 | padding-right: 8px; 143 | } 144 | .btn-group > .btn-lg + .dropdown-toggle { 145 | padding-left: 12px; 146 | padding-right: 12px; 147 | } 148 | 149 | // The clickable button for toggling the menu 150 | // Remove the gradient and set the same inset shadow as the :active state 151 | .btn-group.open .dropdown-toggle { 152 | .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); 153 | 154 | // Show no shadow for `.btn-link` since it has no other button styles. 155 | &.btn-link { 156 | .box-shadow(none); 157 | } 158 | } 159 | 160 | 161 | // Reposition the caret 162 | .btn .caret { 163 | margin-left: 0; 164 | } 165 | // Carets in other button sizes 166 | .btn-lg .caret { 167 | border-width: @caret-width-large @caret-width-large 0; 168 | border-bottom-width: 0; 169 | } 170 | // Upside down carets for .dropup 171 | .dropup .btn-lg .caret { 172 | border-width: 0 @caret-width-large @caret-width-large; 173 | } 174 | 175 | 176 | // Vertical button groups 177 | // ---------------------- 178 | 179 | .btn-group-vertical { 180 | > .btn, 181 | > .btn-group { 182 | display: block; 183 | float: none; 184 | width: 100%; 185 | max-width: 100%; 186 | } 187 | 188 | // Clear floats so dropdown menus can be properly placed 189 | > .btn-group { 190 | .clearfix(); 191 | > .btn { 192 | float: none; 193 | } 194 | } 195 | 196 | > .btn + .btn, 197 | > .btn + .btn-group, 198 | > .btn-group + .btn, 199 | > .btn-group + .btn-group { 200 | margin-top: -1px; 201 | margin-left: 0; 202 | } 203 | } 204 | 205 | .btn-group-vertical > .btn { 206 | &:not(:first-child):not(:last-child) { 207 | border-radius: 0; 208 | } 209 | &:first-child:not(:last-child) { 210 | border-top-right-radius: @border-radius-base; 211 | .border-bottom-radius(0); 212 | } 213 | &:last-child:not(:first-child) { 214 | border-bottom-left-radius: @border-radius-base; 215 | .border-top-radius(0); 216 | } 217 | } 218 | .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { 219 | border-radius: 0; 220 | } 221 | .btn-group-vertical > .btn-group:first-child { 222 | > .btn:last-child, 223 | > .dropdown-toggle { 224 | .border-bottom-radius(0); 225 | } 226 | } 227 | .btn-group-vertical > .btn-group:last-child > .btn:first-child { 228 | .border-top-radius(0); 229 | } 230 | 231 | 232 | 233 | // Justified button groups 234 | // ---------------------- 235 | 236 | .btn-group-justified { 237 | display: table; 238 | width: 100%; 239 | table-layout: fixed; 240 | border-collapse: separate; 241 | .btn { 242 | float: none; 243 | display: table-cell; 244 | width: 1%; 245 | } 246 | } 247 | 248 | 249 | // Checkbox and radio options 250 | [data-toggle="buttons"] > .btn > input[type="radio"], 251 | [data-toggle="buttons"] > .btn > input[type="checkbox"] { 252 | display: none; 253 | } 254 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/navs.less: -------------------------------------------------------------------------------- 1 | // 2 | // Navs 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | // -------------------------------------------------- 8 | 9 | .nav { 10 | margin-bottom: 0; 11 | padding-left: 0; // Override default ul/ol 12 | list-style: none; 13 | .clearfix(); 14 | 15 | > li { 16 | position: relative; 17 | display: block; 18 | 19 | > a { 20 | position: relative; 21 | display: block; 22 | padding: @nav-link-padding; 23 | &:hover, 24 | &:focus { 25 | text-decoration: none; 26 | background-color: @nav-link-hover-bg; 27 | } 28 | } 29 | 30 | // Disabled state sets text to gray and nukes hover/tab effects 31 | &.disabled > a { 32 | color: @nav-disabled-link-color; 33 | 34 | &:hover, 35 | &:focus { 36 | color: @nav-disabled-link-hover-color; 37 | text-decoration: none; 38 | background-color: transparent; 39 | cursor: not-allowed; 40 | } 41 | } 42 | } 43 | 44 | // Open dropdowns 45 | .open > a { 46 | &, 47 | &:hover, 48 | &:focus { 49 | background-color: @nav-link-hover-bg; 50 | border-color: @link-color; 51 | 52 | .caret { 53 | border-top-color: @link-hover-color; 54 | border-bottom-color: @link-hover-color; 55 | } 56 | } 57 | } 58 | 59 | // Nav dividers (deprecated with v3.0.1) 60 | // 61 | // This should have been removed in v3 with the dropping of `.nav-list`, but 62 | // we missed it. We don't currently support this anywhere, but in the interest 63 | // of maintaining backward compatibility in case you use it, it's deprecated. 64 | .nav-divider { 65 | .nav-divider(); 66 | } 67 | 68 | // Prevent IE8 from misplacing imgs 69 | // 70 | // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989 71 | > li > a > img { 72 | max-width: none; 73 | } 74 | } 75 | 76 | 77 | // Tabs 78 | // ------------------------- 79 | 80 | // Give the tabs something to sit on 81 | .nav-tabs { 82 | border-bottom: 1px solid @nav-tabs-border-color; 83 | > li { 84 | float: left; 85 | // Make the list-items overlay the bottom border 86 | margin-bottom: -1px; 87 | 88 | // Actual tabs (as links) 89 | > a { 90 | margin-right: 2px; 91 | line-height: @line-height-base; 92 | border: 1px solid transparent; 93 | border-radius: @border-radius-base @border-radius-base 0 0; 94 | &:hover { 95 | border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color; 96 | } 97 | } 98 | 99 | // Active state, and it's :hover to override normal :hover 100 | &.active > a { 101 | &, 102 | &:hover, 103 | &:focus { 104 | color: @nav-tabs-active-link-hover-color; 105 | background-color: @nav-tabs-active-link-hover-bg; 106 | border: 1px solid @nav-tabs-active-link-hover-border-color; 107 | border-bottom-color: transparent; 108 | cursor: default; 109 | } 110 | } 111 | } 112 | // pulling this in mainly for less shorthand 113 | &.nav-justified { 114 | .nav-justified(); 115 | .nav-tabs-justified(); 116 | } 117 | } 118 | 119 | 120 | // Pills 121 | // ------------------------- 122 | .nav-pills { 123 | > li { 124 | float: left; 125 | 126 | // Links rendered as pills 127 | > a { 128 | border-radius: @nav-pills-border-radius; 129 | } 130 | + li { 131 | margin-left: 2px; 132 | } 133 | 134 | // Active state 135 | &.active > a { 136 | &, 137 | &:hover, 138 | &:focus { 139 | color: @nav-pills-active-link-hover-color; 140 | background-color: @nav-pills-active-link-hover-bg; 141 | 142 | .caret { 143 | border-top-color: @nav-pills-active-link-hover-color; 144 | border-bottom-color: @nav-pills-active-link-hover-color; 145 | } 146 | } 147 | } 148 | } 149 | } 150 | 151 | 152 | // Stacked pills 153 | .nav-stacked { 154 | > li { 155 | float: none; 156 | + li { 157 | margin-top: 2px; 158 | margin-left: 0; // no need for this gap between nav items 159 | } 160 | } 161 | } 162 | 163 | 164 | // Nav variations 165 | // -------------------------------------------------- 166 | 167 | // Justified nav links 168 | // ------------------------- 169 | 170 | .nav-justified { 171 | width: 100%; 172 | 173 | > li { 174 | float: none; 175 | > a { 176 | text-align: center; 177 | margin-bottom: 5px; 178 | } 179 | } 180 | 181 | > .dropdown .dropdown-menu { 182 | top: auto; 183 | left: auto; 184 | } 185 | 186 | @media (min-width: @screen-sm-min) { 187 | > li { 188 | display: table-cell; 189 | width: 1%; 190 | > a { 191 | margin-bottom: 0; 192 | } 193 | } 194 | } 195 | } 196 | 197 | // Move borders to anchors instead of bottom of list 198 | // 199 | // Mixin for adding on top the shared `.nav-justified` styles for our tabs 200 | .nav-tabs-justified { 201 | border-bottom: 0; 202 | 203 | > li > a { 204 | // Override margin from .nav-tabs 205 | margin-right: 0; 206 | border-radius: @border-radius-base; 207 | } 208 | 209 | > .active > a, 210 | > .active > a:hover, 211 | > .active > a:focus { 212 | border: 1px solid @nav-tabs-justified-link-border-color; 213 | } 214 | 215 | @media (min-width: @screen-sm-min) { 216 | > li > a { 217 | border-bottom: 1px solid @nav-tabs-justified-link-border-color; 218 | border-radius: @border-radius-base @border-radius-base 0 0; 219 | } 220 | > .active > a, 221 | > .active > a:hover, 222 | > .active > a:focus { 223 | border-bottom-color: @nav-tabs-justified-active-link-border-color; 224 | } 225 | } 226 | } 227 | 228 | 229 | // Tabbable tabs 230 | // ------------------------- 231 | 232 | // Hide tabbable panes to start, show them when `.active` 233 | .tab-content { 234 | > .tab-pane { 235 | display: none; 236 | } 237 | > .active { 238 | display: block; 239 | } 240 | } 241 | 242 | 243 | // Dropdowns 244 | // ------------------------- 245 | 246 | // Make dropdown carets use link color in navs 247 | .nav .caret { 248 | border-top-color: @link-color; 249 | border-bottom-color: @link-color; 250 | } 251 | .nav a:hover .caret { 252 | border-top-color: @link-hover-color; 253 | border-bottom-color: @link-hover-color; 254 | } 255 | 256 | // Specific dropdowns 257 | .nav-tabs .dropdown-menu { 258 | // make dropdown border overlap tab border 259 | margin-top: -1px; 260 | // Remove the top rounded corners here since there is a hard edge above the menu 261 | .border-top-radius(0); 262 | } 263 | -------------------------------------------------------------------------------- /Applications/Statistics/Clients/StatisticClient.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | /** 15 | * 统计客户端 16 | * @author workerman.net 17 | */ 18 | class StatisticClient 19 | { 20 | /** 21 | * [module=>[interface=>time_start, interface=>time_start ...], module=>[interface=>time_start ..], ... ] 22 | * @var array 23 | */ 24 | protected static $timeMap = array(); 25 | 26 | /** 27 | * 模块接口上报消耗时间记时 28 | * @param string $module 29 | * @param string $interface 30 | * @return void 31 | */ 32 | public static function tick($module = '', $interface = '') 33 | { 34 | return self::$timeMap[$module][$interface] = microtime(true); 35 | } 36 | 37 | /** 38 | * 上报统计数据 39 | * @param string $module 40 | * @param string $interface 41 | * @param bool $success 42 | * @param int $code 43 | * @param string $msg 44 | * @param string $report_address 45 | * @return boolean 46 | */ 47 | public static function report($module, $interface, $success, $code, $msg, $report_address = '') 48 | { 49 | $report_address = $report_address ? $report_address : 'udp://127.0.0.1:55656'; 50 | if(isset(self::$timeMap[$module][$interface]) && self::$timeMap[$module][$interface] > 0) 51 | { 52 | $time_start = self::$timeMap[$module][$interface]; 53 | self::$timeMap[$module][$interface] = 0; 54 | } 55 | else if(isset(self::$timeMap['']['']) && self::$timeMap[''][''] > 0) 56 | { 57 | $time_start = self::$timeMap['']['']; 58 | self::$timeMap[''][''] = 0; 59 | } 60 | else 61 | { 62 | $time_start = microtime(true); 63 | } 64 | 65 | $cost_time = microtime(true) - $time_start; 66 | 67 | $bin_data = StatisticProtocol::encode($module, $interface, $cost_time, $success, $code, $msg); 68 | 69 | return self::sendData($report_address, $bin_data); 70 | } 71 | 72 | /** 73 | * 发送数据给统计系统 74 | * @param string $address 75 | * @param string $buffer 76 | * @return boolean 77 | */ 78 | public static function sendData($address, $buffer) 79 | { 80 | $socket = stream_socket_client($address); 81 | if(!$socket) 82 | { 83 | return false; 84 | } 85 | return stream_socket_sendto($socket, $buffer) == strlen($buffer); 86 | } 87 | 88 | } 89 | 90 | /** 91 | * 92 | * struct statisticPortocol 93 | * { 94 | * unsigned char module_name_len; 95 | * unsigned char interface_name_len; 96 | * float cost_time; 97 | * unsigned char success; 98 | * int code; 99 | * unsigned short msg_len; 100 | * unsigned int time; 101 | * char[module_name_len] module_name; 102 | * char[interface_name_len] interface_name; 103 | * char[msg_len] msg; 104 | * } 105 | * 106 | * @author workerman.net 107 | */ 108 | class StatisticProtocol 109 | { 110 | /** 111 | * 包头长度 112 | * @var integer 113 | */ 114 | const PACKAGE_FIXED_LENGTH = 17; 115 | 116 | /** 117 | * udp 包最大长度 118 | * @var integer 119 | */ 120 | const MAX_UDP_PACKGE_SIZE = 65507; 121 | 122 | /** 123 | * char类型能保存的最大数值 124 | * @var integer 125 | */ 126 | const MAX_CHAR_VALUE = 255; 127 | 128 | /** 129 | * usigned short 能保存的最大数值 130 | * @var integer 131 | */ 132 | const MAX_UNSIGNED_SHORT_VALUE = 65535; 133 | 134 | /** 135 | * 编码 136 | * @param string $module 137 | * @param string $interface 138 | * @param float $cost_time 139 | * @param int $success 140 | * @param int $code 141 | * @param string $msg 142 | * @return string 143 | */ 144 | public static function encode($module, $interface , $cost_time, $success, $code = 0,$msg = '') 145 | { 146 | // 防止模块名过长 147 | if(strlen($module) > self::MAX_CHAR_VALUE) 148 | { 149 | $module = substr($module, 0, self::MAX_CHAR_VALUE); 150 | } 151 | 152 | // 防止接口名过长 153 | if(strlen($interface) > self::MAX_CHAR_VALUE) 154 | { 155 | $interface = substr($interface, 0, self::MAX_CHAR_VALUE); 156 | } 157 | 158 | // 防止msg过长 159 | $module_name_length = strlen($module); 160 | $interface_name_length = strlen($interface); 161 | $avalible_size = self::MAX_UDP_PACKGE_SIZE - self::PACKAGE_FIXED_LENGTH - $module_name_length - $interface_name_length; 162 | if(strlen($msg) > $avalible_size) 163 | { 164 | $msg = substr($msg, 0, $avalible_size); 165 | } 166 | 167 | // 打包 168 | return pack('CCfCNnN', $module_name_length, $interface_name_length, $cost_time, $success ? 1 : 0, $code, strlen($msg), time()).$module.$interface.$msg; 169 | } 170 | 171 | /** 172 | * 解包 173 | * @param string $bin_data 174 | * @return array 175 | */ 176 | public static function decode($bin_data) 177 | { 178 | // 解包 179 | $data = unpack("Cmodule_name_len/Cinterface_name_len/fcost_time/Csuccess/Ncode/nmsg_len/Ntime", $bin_data); 180 | $module = substr($bin_data, self::PACKAGE_FIXED_LENGTH, $data['module_name_len']); 181 | $interface = substr($bin_data, self::PACKAGE_FIXED_LENGTH + $data['module_name_len'], $data['interface_name_len']); 182 | $msg = substr($bin_data, self::PACKAGE_FIXED_LENGTH + $data['module_name_len'] + $data['interface_name_len']); 183 | return array( 184 | 'module' => $module, 185 | 'interface' => $interface, 186 | 'cost_time' => $data['cost_time'], 187 | 'success' => $data['success'], 188 | 'time' => $data['time'], 189 | 'code' => $data['code'], 190 | 'msg' => $msg, 191 | ); 192 | } 193 | 194 | } 195 | 196 | if(PHP_SAPI == 'cli' && isset($argv[0]) && $argv[0] == basename(__FILE__)) 197 | { 198 | StatisticClient::tick("TestModule", 'TestInterface'); 199 | usleep(rand(10000, 600000)); 200 | $success = rand(0,1); 201 | $code = rand(300, 400); 202 | $msg = '这个是测试消息'; 203 | var_export(StatisticClient::report('TestModule', 'TestInterface', $success, $code, $msg));; 204 | } 205 | -------------------------------------------------------------------------------- /Applications/Statistics/Views/main.tpl.php: -------------------------------------------------------------------------------- 1 |
      2 |
      3 |
      4 | 32 |
      33 |
      34 |
      35 |
      36 | 37 |
      38 | 39 | 40 |
      41 | 42 |
      43 | 44 | 45 |
      46 | 47 |
      48 |
      49 | 50 |
      51 |
      52 |
      53 |
      54 |
      55 |
      56 |
      57 |
      58 |
      59 |
      60 |
      61 |
      62 |
      63 |
      64 |
      65 |
      66 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 |
      时间调用总数平均耗时成功调用总数成功平均耗时失败调用总数失败平均耗时成功率
      281 |
      282 |
      283 |
      284 | -------------------------------------------------------------------------------- /Applications/Statistics/Modules/statistic.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Statistics\Modules; 15 | function statistic($module, $interface, $date, $start_time, $offset) 16 | { 17 | $err_msg = ''; 18 | $today = date('Y-m-d'); 19 | $time_now = time(); 20 | multiRequestStAndModules($module, $interface, $date); 21 | $all_st_str = ''; 22 | if(is_array(\Statistics\Lib\Cache::$statisticDataCache['statistic'])) 23 | { 24 | foreach(\Statistics\Lib\Cache::$statisticDataCache['statistic'] as $ip=>$st_str) 25 | { 26 | $all_st_str .= $st_str; 27 | } 28 | } 29 | 30 | $code_map = array(); 31 | $data = formatSt($all_st_str, $date, $code_map); 32 | $interface_name = "$module::$interface"; 33 | $success_series_data = $fail_series_data = $success_time_series_data = $fail_time_series_data = array(); 34 | $total_count = $fail_count = 0; 35 | foreach($data as $time_point=>$item) 36 | { 37 | if($item['total_count']) 38 | { 39 | $success_series_data[] = "[".($time_point*1000).",{$item['total_count']}]"; 40 | $total_count += $item['total_count']; 41 | } 42 | $fail_series_data[] = "[".($time_point*1000).",{$item['fail_count']}]"; 43 | $fail_count += $item['fail_count']; 44 | if($item['total_avg_time']) 45 | { 46 | $success_time_series_data[] = "[".($time_point*1000).",{$item['total_avg_time']}]"; 47 | } 48 | $fail_time_series_data[] = "[".($time_point*1000).",{$item['fail_avg_time']}]"; 49 | } 50 | $success_series_data = implode(',', $success_series_data); 51 | $fail_series_data = implode(',', $fail_series_data); 52 | $success_time_series_data = implode(',', $success_time_series_data); 53 | $fail_time_series_data = implode(',', $fail_time_series_data); 54 | 55 | unset($_GET['start_time'], $_GET['end_time'], $_GET['date'], $_GET['fn']); 56 | $query = http_build_query($_GET); 57 | 58 | // 删除末尾0的记录 59 | if($today == $date) 60 | { 61 | while(!empty($data) && ($item = end($data)) && $item['total_count'] == 0 && ($key = key($data)) && $time_now < $key) 62 | { 63 | unset($data[$key]); 64 | } 65 | } 66 | 67 | $table_data = $html_class = ''; 68 | if($data) 69 | { 70 | $first_line = true; 71 | foreach($data as $item) 72 | { 73 | if($first_line) 74 | { 75 | $first_line = false; 76 | } 77 | $html_class = 'class="danger"'; 78 | if($item['total_count'] == 0) 79 | { 80 | $html_class = ''; 81 | $total_count_zero_start = true; 82 | if (empty($zero_time_start)) $zero_time_start = $item['time']; 83 | continue; 84 | } 85 | else 86 | { 87 | if ($total_count_zero_start) { 88 | $zero_time_end = $item['time']; 89 | $zero_time_combine = $zero_time_start . ' ~ ' . $zero_time_end; 90 | $total_count_zero_start = false; 91 | $zero_time_start = $zero_time_end = ''; 92 | $table_data .= "\n 93 | {$zero_time_combine} 94 | 0 95 | 0 96 | 0 97 | 0 98 | 0 99 | 0 100 | 0% 101 | 102 | "; 103 | } 104 | 105 | if($item['precent']>=99.99) 106 | { 107 | $html_class = 'class="success"'; 108 | } 109 | elseif($item['precent']>=99) 110 | { 111 | $html_class = ''; 112 | } 113 | elseif($item['precent']>=98) 114 | { 115 | $html_class = 'class="warning"'; 116 | } 117 | } 118 | 119 | $table_data .= "\n 120 | {$item['time']} 121 | {$item['total_count']} 122 | {$item['total_avg_time']} 123 | {$item['suc_count']} 124 | {$item['suc_avg_time']} 125 | ".($item['fail_count']>0?("{$item['fail_count']}"):$item['fail_count'])." 126 | {$item['fail_avg_time']} 127 | {$item['precent']}% 128 | 129 | "; 130 | } 131 | } 132 | 133 | // date btn 134 | $date_btn_str = ''; 135 | for($i=13;$i>=1;$i--) 136 | { 137 | $the_time = strtotime("-$i day"); 138 | $the_date = date('Y-m-d',$the_time); 139 | $html_the_date = $date == $the_date ? "$the_date" : $the_date; 140 | $date_btn_str .= ''.$html_the_date.''; 141 | if($i == 7) 142 | { 143 | $date_btn_str .= '
      '; 144 | } 145 | } 146 | $the_date = date('Y-m-d'); 147 | $html_the_date = $date == $the_date ? "$the_date" : $the_date; 148 | $date_btn_str .= ''.$html_the_date.''; 149 | 150 | $module_str =''; 151 | foreach(\Statistics\Lib\Cache::$modulesDataCache as $mod => $interfaces) 152 | { 153 | if($mod == 'WorkerMan') 154 | { 155 | continue; 156 | } 157 | $module_str .= '
    5. '.$mod.'
    6. '; 158 | if($module == $mod) 159 | { 160 | foreach ($interfaces as $if) 161 | { 162 | $module_str .= '
    7.   '.$if.'
    8. '; 163 | } 164 | } 165 | } 166 | 167 | if( \Statistics\Lib\Cache::$lastFailedIpArray) 168 | { 169 | $err_msg = '无法从以下数据源获取数据:'; 170 | foreach (\Statistics\Lib\Cache::$lastFailedIpArray as $ip) 171 | { 172 | $err_msg .= $ip.'::'.\Statistics\Config::$ProviderPort . ' '; 173 | } 174 | } 175 | 176 | include ST_ROOT . '/Views/header.tpl.php'; 177 | include ST_ROOT . '/Views/statistic.tpl.php'; 178 | include ST_ROOT . '/Views/footer.tpl.php'; 179 | } 180 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/theme.less: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Load core variables and mixins 4 | // -------------------------------------------------- 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | 9 | 10 | 11 | // 12 | // Buttons 13 | // -------------------------------------------------- 14 | 15 | // Common styles 16 | .btn-default, 17 | .btn-primary, 18 | .btn-success, 19 | .btn-info, 20 | .btn-warning, 21 | .btn-danger { 22 | text-shadow: 0 -1px 0 rgba(0,0,0,.2); 23 | @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075); 24 | .box-shadow(@shadow); 25 | 26 | // Reset the shadow 27 | &:active, 28 | &.active { 29 | .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); 30 | } 31 | } 32 | 33 | // Mixin for generating new styles 34 | .btn-styles(@btn-color: #555) { 35 | #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%)); 36 | .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners 37 | background-repeat: repeat-x; 38 | border-color: darken(@btn-color, 14%); 39 | 40 | &:hover, 41 | &:focus { 42 | background-color: darken(@btn-color, 12%); 43 | background-position: 0 -15px; 44 | } 45 | 46 | &:active, 47 | &.active { 48 | background-color: darken(@btn-color, 12%); 49 | border-color: darken(@btn-color, 14%); 50 | } 51 | } 52 | 53 | // Common styles 54 | .btn { 55 | // Remove the gradient for the pressed/active state 56 | &:active, 57 | &.active { 58 | background-image: none; 59 | } 60 | } 61 | 62 | // Apply the mixin to the buttons 63 | .btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; } 64 | .btn-primary { .btn-styles(@btn-primary-bg); } 65 | .btn-success { .btn-styles(@btn-success-bg); } 66 | .btn-warning { .btn-styles(@btn-warning-bg); } 67 | .btn-danger { .btn-styles(@btn-danger-bg); } 68 | .btn-info { .btn-styles(@btn-info-bg); } 69 | 70 | 71 | 72 | // 73 | // Images 74 | // -------------------------------------------------- 75 | 76 | .thumbnail, 77 | .img-thumbnail { 78 | .box-shadow(0 1px 2px rgba(0,0,0,.075)); 79 | } 80 | 81 | 82 | 83 | // 84 | // Dropdowns 85 | // -------------------------------------------------- 86 | 87 | .dropdown-menu > li > a:hover, 88 | .dropdown-menu > li > a:focus { 89 | #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%)); 90 | background-color: darken(@dropdown-link-hover-bg, 5%); 91 | } 92 | .dropdown-menu > .active > a, 93 | .dropdown-menu > .active > a:hover, 94 | .dropdown-menu > .active > a:focus { 95 | #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%)); 96 | background-color: darken(@dropdown-link-active-bg, 5%); 97 | } 98 | 99 | 100 | 101 | // 102 | // Navbar 103 | // -------------------------------------------------- 104 | 105 | // Default navbar 106 | .navbar-default { 107 | #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg); 108 | .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered 109 | border-radius: @navbar-border-radius; 110 | @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075); 111 | .box-shadow(@shadow); 112 | 113 | .navbar-nav > .active > a { 114 | #gradient > .vertical(@start-color: darken(@navbar-default-bg, 5%); @end-color: darken(@navbar-default-bg, 2%)); 115 | .box-shadow(inset 0 3px 9px rgba(0,0,0,.075)); 116 | } 117 | } 118 | .navbar-brand, 119 | .navbar-nav > li > a { 120 | text-shadow: 0 1px 0 rgba(255,255,255,.25); 121 | } 122 | 123 | // Inverted navbar 124 | .navbar-inverse { 125 | #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg); 126 | .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered 127 | 128 | .navbar-nav > .active > a { 129 | #gradient > .vertical(@start-color: @navbar-inverse-bg; @end-color: lighten(@navbar-inverse-bg, 2.5%)); 130 | .box-shadow(inset 0 3px 9px rgba(0,0,0,.25)); 131 | } 132 | 133 | .navbar-brand, 134 | .navbar-nav > li > a { 135 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 136 | } 137 | } 138 | 139 | // Undo rounded corners in static and fixed navbars 140 | .navbar-static-top, 141 | .navbar-fixed-top, 142 | .navbar-fixed-bottom { 143 | border-radius: 0; 144 | } 145 | 146 | 147 | 148 | // 149 | // Alerts 150 | // -------------------------------------------------- 151 | 152 | // Common styles 153 | .alert { 154 | text-shadow: 0 1px 0 rgba(255,255,255,.2); 155 | @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05); 156 | .box-shadow(@shadow); 157 | } 158 | 159 | // Mixin for generating new styles 160 | .alert-styles(@color) { 161 | #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%)); 162 | border-color: darken(@color, 15%); 163 | } 164 | 165 | // Apply the mixin to the alerts 166 | .alert-success { .alert-styles(@alert-success-bg); } 167 | .alert-info { .alert-styles(@alert-info-bg); } 168 | .alert-warning { .alert-styles(@alert-warning-bg); } 169 | .alert-danger { .alert-styles(@alert-danger-bg); } 170 | 171 | 172 | 173 | // 174 | // Progress bars 175 | // -------------------------------------------------- 176 | 177 | // Give the progress background some depth 178 | .progress { 179 | #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg) 180 | } 181 | 182 | // Mixin for generating new styles 183 | .progress-bar-styles(@color) { 184 | #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%)); 185 | } 186 | 187 | // Apply the mixin to the progress bars 188 | .progress-bar { .progress-bar-styles(@progress-bar-bg); } 189 | .progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); } 190 | .progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); } 191 | .progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); } 192 | .progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); } 193 | 194 | 195 | 196 | // 197 | // List groups 198 | // -------------------------------------------------- 199 | 200 | .list-group { 201 | border-radius: @border-radius-base; 202 | .box-shadow(0 1px 2px rgba(0,0,0,.075)); 203 | } 204 | .list-group-item.active, 205 | .list-group-item.active:hover, 206 | .list-group-item.active:focus { 207 | text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%); 208 | #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%)); 209 | border-color: darken(@list-group-active-border, 7.5%); 210 | } 211 | 212 | 213 | 214 | // 215 | // Panels 216 | // -------------------------------------------------- 217 | 218 | // Common styles 219 | .panel { 220 | .box-shadow(0 1px 2px rgba(0,0,0,.05)); 221 | } 222 | 223 | // Mixin for generating new styles 224 | .panel-heading-styles(@color) { 225 | #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%)); 226 | } 227 | 228 | // Apply the mixin to the panel headings only 229 | .panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); } 230 | .panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); } 231 | .panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); } 232 | .panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); } 233 | .panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); } 234 | .panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); } 235 | 236 | 237 | 238 | // 239 | // Wells 240 | // -------------------------------------------------- 241 | 242 | .well { 243 | #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg); 244 | border-color: darken(@well-bg, 10%); 245 | @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1); 246 | .box-shadow(@shadow); 247 | } 248 | -------------------------------------------------------------------------------- /Applications/Statistics/Bootstrap/StatisticWorker.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Bootstrap; 15 | use Workerman\Worker; 16 | use Workerman\Lib\Timer; 17 | use Statistics\Config; 18 | 19 | /** 20 | * 21 | * @author walkor 22 | */ 23 | class StatisticWorker extends Worker 24 | { 25 | /** 26 | * 最大日志buffer,大于这个值就写磁盘 27 | * @var integer 28 | */ 29 | const MAX_LOG_BUFFER_SIZE = 1024000; 30 | 31 | /** 32 | * 多长时间写一次数据到磁盘 33 | * @var integer 34 | */ 35 | const WRITE_PERIOD_LENGTH = 60; 36 | 37 | /** 38 | * 多长时间清理一次老的磁盘数据 39 | * @var integer 40 | */ 41 | const CLEAR_PERIOD_LENGTH = 86400; 42 | 43 | /** 44 | * 数据多长时间过期 45 | * @var integer 46 | */ 47 | const EXPIRED_TIME = 1296000; 48 | 49 | /** 50 | * 统计数据 51 | * ip=>modid=>interface=>['code'=>[xx=>count,xx=>count],'suc_cost_time'=>xx,'fail_cost_time'=>xx, 'suc_count'=>xx, 'fail_count'=>xx] 52 | * @var array 53 | */ 54 | protected $statisticData = array(); 55 | 56 | /** 57 | * 日志的buffer 58 | * @var string 59 | */ 60 | protected $logBuffer = ''; 61 | 62 | /** 63 | * 放统计数据的目录 64 | * @var string 65 | */ 66 | protected $statisticDir = 'statistic/statistic/'; 67 | 68 | /** 69 | * 存放统计日志的目录 70 | * @var string 71 | */ 72 | protected $logDir = 'statistic/log/'; 73 | 74 | /** 75 | * 提供统计查询的socket 76 | * @var resource 77 | */ 78 | protected $providerSocket = null; 79 | 80 | public function __construct($socket_name) 81 | { 82 | parent::__construct($socket_name); 83 | $this->onWorkerStart = array($this, 'onStart'); 84 | $this->onMessage = array($this, 'onMessage'); 85 | $this->onWorkerStop = array($this, 'onStop'); 86 | } 87 | 88 | /** 89 | * 业务处理 90 | * @see Man\Core.SocketWorker::dealProcess() 91 | */ 92 | public function onMessage($connection, $data) 93 | { 94 | // 解码 95 | $module = $data['module']; 96 | $interface = $data['interface']; 97 | $cost_time = $data['cost_time']; 98 | $success = $data['success']; 99 | $time = $data['time']; 100 | $code = $data['code']; 101 | $msg = str_replace("\n", "
      ", $data['msg']); 102 | $ip = $connection->getRemoteIp(); 103 | 104 | // 模块接口统计 105 | $this->collectStatistics($module, $interface, $cost_time, $success, $ip, $code, $msg); 106 | // 全局统计 107 | $this->collectStatistics('WorkerMan', 'Statistics', $cost_time, $success, $ip, $code, $msg); 108 | 109 | // 失败记录日志 110 | if(!$success) 111 | { 112 | $this->logBuffer .= date('Y-m-d H:i:s',$time)."\t$ip\t$module::$interface\tcode:$code\tmsg:$msg\n"; 113 | if(strlen($this->logBuffer) >= self::MAX_LOG_BUFFER_SIZE) 114 | { 115 | $this->writeLogToDisk(); 116 | } 117 | } 118 | } 119 | 120 | /** 121 | * 收集统计数据 122 | * @param string $module 123 | * @param string $interface 124 | * @param float $cost_time 125 | * @param int $success 126 | * @param string $ip 127 | * @param int $code 128 | * @param string $msg 129 | * @return void 130 | */ 131 | protected function collectStatistics($module, $interface , $cost_time, $success, $ip, $code, $msg) 132 | { 133 | // 统计相关信息 134 | if(!isset($this->statisticData[$ip])) 135 | { 136 | $this->statisticData[$ip] = array(); 137 | } 138 | if(!isset($this->statisticData[$ip][$module])) 139 | { 140 | $this->statisticData[$ip][$module] = array(); 141 | } 142 | if(!isset($this->statisticData[$ip][$module][$interface])) 143 | { 144 | $this->statisticData[$ip][$module][$interface] = array('code'=>array(), 'suc_cost_time'=>0, 'fail_cost_time'=>0, 'suc_count'=>0, 'fail_count'=>0); 145 | } 146 | if(!isset($this->statisticData[$ip][$module][$interface]['code'][$code])) 147 | { 148 | $this->statisticData[$ip][$module][$interface]['code'][$code] = 0; 149 | } 150 | $this->statisticData[$ip][$module][$interface]['code'][$code]++; 151 | if($success) 152 | { 153 | $this->statisticData[$ip][$module][$interface]['suc_cost_time'] += $cost_time; 154 | $this->statisticData[$ip][$module][$interface]['suc_count'] ++; 155 | } 156 | else 157 | { 158 | $this->statisticData[$ip][$module][$interface]['fail_cost_time'] += $cost_time; 159 | $this->statisticData[$ip][$module][$interface]['fail_count'] ++; 160 | } 161 | } 162 | 163 | /** 164 | * 将统计数据写入磁盘 165 | * @return void 166 | */ 167 | public function writeStatisticsToDisk() 168 | { 169 | $time = time(); 170 | // 循环将每个ip的统计数据写入磁盘 171 | foreach($this->statisticData as $ip => $mod_if_data) 172 | { 173 | foreach($mod_if_data as $module=>$items) 174 | { 175 | // 文件夹不存在则创建一个 176 | $file_dir = Config::$dataPath . $this->statisticDir.$module; 177 | if(!is_dir($file_dir)) 178 | { 179 | umask(0); 180 | mkdir($file_dir, 0777, true); 181 | } 182 | // 依次写入磁盘 183 | foreach($items as $interface=>$data) 184 | { 185 | file_put_contents($file_dir. "/{$interface}.".date('Y-m-d'), "$ip\t$time\t{$data['suc_count']}\t{$data['suc_cost_time']}\t{$data['fail_count']}\t{$data['fail_cost_time']}\t".json_encode($data['code'])."\n", FILE_APPEND | LOCK_EX); 186 | } 187 | } 188 | } 189 | // 清空统计 190 | $this->statisticData = array(); 191 | } 192 | 193 | /** 194 | * 将日志数据写入磁盘 195 | * @return void 196 | */ 197 | public function writeLogToDisk() 198 | { 199 | // 没有统计数据则返回 200 | if(empty($this->logBuffer)) 201 | { 202 | return; 203 | } 204 | // 写入磁盘 205 | file_put_contents(Config::$dataPath . $this->logDir . date('Y-m-d'), $this->logBuffer, FILE_APPEND | LOCK_EX); 206 | $this->logBuffer = ''; 207 | } 208 | 209 | /** 210 | * 初始化 211 | * 统计目录检查 212 | * 初始化任务 213 | * @see Man\Core.SocketWorker::onStart() 214 | */ 215 | protected function onStart() 216 | { 217 | // 初始化目录 218 | umask(0); 219 | $statistic_dir = Config::$dataPath . $this->statisticDir; 220 | if(!is_dir($statistic_dir)) 221 | { 222 | mkdir($statistic_dir, 0777, true); 223 | } 224 | $log_dir = Config::$dataPath . $this->logDir; 225 | if(!is_dir($log_dir)) 226 | { 227 | mkdir($log_dir, 0777, true); 228 | } 229 | // 定时保存统计数据 230 | Timer::add(self::WRITE_PERIOD_LENGTH, array($this, 'writeStatisticsToDisk')); 231 | Timer::add(self::WRITE_PERIOD_LENGTH, array($this, 'writeLogToDisk')); 232 | // 定时清理不用的统计数据 233 | Timer::add(self::CLEAR_PERIOD_LENGTH, array($this, 'clearDisk'), array(Config::$dataPath . $this->statisticDir, self::EXPIRED_TIME)); 234 | Timer::add(self::CLEAR_PERIOD_LENGTH, array($this, 'clearDisk'), array(Config::$dataPath . $this->logDir, self::EXPIRED_TIME)); 235 | 236 | } 237 | 238 | /** 239 | * 进程停止时需要将数据写入磁盘 240 | * @see Man\Core.SocketWorker::onStop() 241 | */ 242 | protected function onStop() 243 | { 244 | $this->writeLogToDisk(); 245 | $this->writeStatisticsToDisk(); 246 | } 247 | 248 | /** 249 | * 清除磁盘数据 250 | * @param string $file 251 | * @param int $exp_time 252 | */ 253 | public function clearDisk($file = null, $exp_time = 86400) 254 | { 255 | $time_now = time(); 256 | if(is_file($file)) 257 | { 258 | $mtime = filemtime($file); 259 | if(!$mtime) 260 | { 261 | $this->notice("filemtime $file fail"); 262 | return; 263 | } 264 | if($time_now - $mtime > $exp_time) 265 | { 266 | unlink($file); 267 | } 268 | return; 269 | } 270 | foreach (glob($file."/*") as $file_name) 271 | { 272 | $this->clearDisk($file_name, $exp_time); 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/normalize.less: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.1.3 | MIT License | git.io/normalize */ 2 | 3 | // ========================================================================== 4 | // HTML5 display definitions 5 | // ========================================================================== 6 | 7 | // 8 | // Correct `block` display not defined in IE 8/9. 9 | // 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | main, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | 26 | // 27 | // Correct `inline-block` display not defined in IE 8/9. 28 | // 29 | 30 | audio, 31 | canvas, 32 | video { 33 | display: inline-block; 34 | } 35 | 36 | // 37 | // Prevent modern browsers from displaying `audio` without controls. 38 | // Remove excess height in iOS 5 devices. 39 | // 40 | 41 | audio:not([controls]) { 42 | display: none; 43 | height: 0; 44 | } 45 | 46 | // 47 | // Address `[hidden]` styling not present in IE 8/9. 48 | // Hide the `template` element in IE, Safari, and Firefox < 22. 49 | // 50 | 51 | [hidden], 52 | template { 53 | display: none; 54 | } 55 | 56 | // ========================================================================== 57 | // Base 58 | // ========================================================================== 59 | 60 | // 61 | // 1. Set default font family to sans-serif. 62 | // 2. Prevent iOS text size adjust after orientation change, without disabling 63 | // user zoom. 64 | // 65 | 66 | html { 67 | font-family: sans-serif; // 1 68 | -ms-text-size-adjust: 100%; // 2 69 | -webkit-text-size-adjust: 100%; // 2 70 | } 71 | 72 | // 73 | // Remove default margin. 74 | // 75 | 76 | body { 77 | margin: 0; 78 | } 79 | 80 | // ========================================================================== 81 | // Links 82 | // ========================================================================== 83 | 84 | // 85 | // Remove the gray background color from active links in IE 10. 86 | // 87 | 88 | a { 89 | background: transparent; 90 | } 91 | 92 | // 93 | // Address `outline` inconsistency between Chrome and other browsers. 94 | // 95 | 96 | a:focus { 97 | outline: thin dotted; 98 | } 99 | 100 | // 101 | // Improve readability when focused and also mouse hovered in all browsers. 102 | // 103 | 104 | a:active, 105 | a:hover { 106 | outline: 0; 107 | } 108 | 109 | // ========================================================================== 110 | // Typography 111 | // ========================================================================== 112 | 113 | // 114 | // Address variable `h1` font-size and margin within `section` and `article` 115 | // contexts in Firefox 4+, Safari 5, and Chrome. 116 | // 117 | 118 | h1 { 119 | font-size: 2em; 120 | margin: 0.67em 0; 121 | } 122 | 123 | // 124 | // Address styling not present in IE 8/9, Safari 5, and Chrome. 125 | // 126 | 127 | abbr[title] { 128 | border-bottom: 1px dotted; 129 | } 130 | 131 | // 132 | // Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 133 | // 134 | 135 | b, 136 | strong { 137 | font-weight: bold; 138 | } 139 | 140 | // 141 | // Address styling not present in Safari 5 and Chrome. 142 | // 143 | 144 | dfn { 145 | font-style: italic; 146 | } 147 | 148 | // 149 | // Address differences between Firefox and other browsers. 150 | // 151 | 152 | hr { 153 | -moz-box-sizing: content-box; 154 | box-sizing: content-box; 155 | height: 0; 156 | } 157 | 158 | // 159 | // Address styling not present in IE 8/9. 160 | // 161 | 162 | mark { 163 | background: #ff0; 164 | color: #000; 165 | } 166 | 167 | // 168 | // Correct font family set oddly in Safari 5 and Chrome. 169 | // 170 | 171 | code, 172 | kbd, 173 | pre, 174 | samp { 175 | font-family: monospace, serif; 176 | font-size: 1em; 177 | } 178 | 179 | // 180 | // Improve readability of pre-formatted text in all browsers. 181 | // 182 | 183 | pre { 184 | white-space: pre-wrap; 185 | } 186 | 187 | // 188 | // Set consistent quote types. 189 | // 190 | 191 | q { 192 | quotes: "\201C" "\201D" "\2018" "\2019"; 193 | } 194 | 195 | // 196 | // Address inconsistent and variable font size in all browsers. 197 | // 198 | 199 | small { 200 | font-size: 80%; 201 | } 202 | 203 | // 204 | // Prevent `sub` and `sup` affecting `line-height` in all browsers. 205 | // 206 | 207 | sub, 208 | sup { 209 | font-size: 75%; 210 | line-height: 0; 211 | position: relative; 212 | vertical-align: baseline; 213 | } 214 | 215 | sup { 216 | top: -0.5em; 217 | } 218 | 219 | sub { 220 | bottom: -0.25em; 221 | } 222 | 223 | // ========================================================================== 224 | // Embedded content 225 | // ========================================================================== 226 | 227 | // 228 | // Remove border when inside `a` element in IE 8/9. 229 | // 230 | 231 | img { 232 | border: 0; 233 | } 234 | 235 | // 236 | // Correct overflow displayed oddly in IE 9. 237 | // 238 | 239 | svg:not(:root) { 240 | overflow: hidden; 241 | } 242 | 243 | // ========================================================================== 244 | // Figures 245 | // ========================================================================== 246 | 247 | // 248 | // Address margin not present in IE 8/9 and Safari 5. 249 | // 250 | 251 | figure { 252 | margin: 0; 253 | } 254 | 255 | // ========================================================================== 256 | // Forms 257 | // ========================================================================== 258 | 259 | // 260 | // Define consistent border, margin, and padding. 261 | // 262 | 263 | fieldset { 264 | border: 1px solid #c0c0c0; 265 | margin: 0 2px; 266 | padding: 0.35em 0.625em 0.75em; 267 | } 268 | 269 | // 270 | // 1. Correct `color` not being inherited in IE 8/9. 271 | // 2. Remove padding so people aren't caught out if they zero out fieldsets. 272 | // 273 | 274 | legend { 275 | border: 0; // 1 276 | padding: 0; // 2 277 | } 278 | 279 | // 280 | // 1. Correct font family not being inherited in all browsers. 281 | // 2. Correct font size not being inherited in all browsers. 282 | // 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. 283 | // 284 | 285 | button, 286 | input, 287 | select, 288 | textarea { 289 | font-family: inherit; // 1 290 | font-size: 100%; // 2 291 | margin: 0; // 3 292 | } 293 | 294 | // 295 | // Address Firefox 4+ setting `line-height` on `input` using `!important` in 296 | // the UA stylesheet. 297 | // 298 | 299 | button, 300 | input { 301 | line-height: normal; 302 | } 303 | 304 | // 305 | // Address inconsistent `text-transform` inheritance for `button` and `select`. 306 | // All other form control elements do not inherit `text-transform` values. 307 | // Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. 308 | // Correct `select` style inheritance in Firefox 4+ and Opera. 309 | // 310 | 311 | button, 312 | select { 313 | text-transform: none; 314 | } 315 | 316 | // 317 | // 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 318 | // and `video` controls. 319 | // 2. Correct inability to style clickable `input` types in iOS. 320 | // 3. Improve usability and consistency of cursor style between image-type 321 | // `input` and others. 322 | // 323 | 324 | button, 325 | html input[type="button"], // 1 326 | input[type="reset"], 327 | input[type="submit"] { 328 | -webkit-appearance: button; // 2 329 | cursor: pointer; // 3 330 | } 331 | 332 | // 333 | // Re-set default cursor for disabled elements. 334 | // 335 | 336 | button[disabled], 337 | html input[disabled] { 338 | cursor: default; 339 | } 340 | 341 | // 342 | // 1. Address box sizing set to `content-box` in IE 8/9/10. 343 | // 2. Remove excess padding in IE 8/9/10. 344 | // 345 | 346 | input[type="checkbox"], 347 | input[type="radio"] { 348 | box-sizing: border-box; // 1 349 | padding: 0; // 2 350 | } 351 | 352 | // 353 | // 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 354 | // 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 355 | // (include `-moz` to future-proof). 356 | // 357 | 358 | input[type="search"] { 359 | -webkit-appearance: textfield; // 1 360 | -moz-box-sizing: content-box; 361 | -webkit-box-sizing: content-box; // 2 362 | box-sizing: content-box; 363 | } 364 | 365 | // 366 | // Remove inner padding and search cancel button in Safari 5 and Chrome 367 | // on OS X. 368 | // 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | // 376 | // Remove inner padding and border in Firefox 4+. 377 | // 378 | 379 | button::-moz-focus-inner, 380 | input::-moz-focus-inner { 381 | border: 0; 382 | padding: 0; 383 | } 384 | 385 | // 386 | // 1. Remove default vertical scrollbar in IE 8/9. 387 | // 2. Improve readability and alignment in all browsers. 388 | // 389 | 390 | textarea { 391 | overflow: auto; // 1 392 | vertical-align: top; // 2 393 | } 394 | 395 | // ========================================================================== 396 | // Tables 397 | // ========================================================================== 398 | 399 | // 400 | // Remove most spacing between table cells. 401 | // 402 | 403 | table { 404 | border-collapse: collapse; 405 | border-spacing: 0; 406 | } 407 | -------------------------------------------------------------------------------- /Applications/Statistics/Web/less/forms.less: -------------------------------------------------------------------------------- 1 | // 2 | // Forms 3 | // -------------------------------------------------- 4 | 5 | 6 | // Normalize non-controls 7 | // 8 | // Restyle and baseline non-control form elements. 9 | 10 | fieldset { 11 | padding: 0; 12 | margin: 0; 13 | border: 0; 14 | } 15 | 16 | legend { 17 | display: block; 18 | width: 100%; 19 | padding: 0; 20 | margin-bottom: @line-height-computed; 21 | font-size: (@font-size-base * 1.5); 22 | line-height: inherit; 23 | color: @legend-color; 24 | border: 0; 25 | border-bottom: 1px solid @legend-border-color; 26 | } 27 | 28 | label { 29 | display: inline-block; 30 | margin-bottom: 5px; 31 | font-weight: bold; 32 | } 33 | 34 | 35 | // Normalize form controls 36 | 37 | // Override content-box in Normalize (* isn't specific enough) 38 | input[type="search"] { 39 | .box-sizing(border-box); 40 | } 41 | 42 | // Position radios and checkboxes better 43 | input[type="radio"], 44 | input[type="checkbox"] { 45 | margin: 4px 0 0; 46 | margin-top: 1px \9; /* IE8-9 */ 47 | line-height: normal; 48 | } 49 | 50 | // Set the height of select and file controls to match text inputs 51 | input[type="file"] { 52 | display: block; 53 | } 54 | 55 | // Make multiple select elements height not fixed 56 | select[multiple], 57 | select[size] { 58 | height: auto; 59 | } 60 | 61 | // Fix optgroup Firefox bug per https://github.com/twbs/bootstrap/issues/7611 62 | select optgroup { 63 | font-size: inherit; 64 | font-style: inherit; 65 | font-family: inherit; 66 | } 67 | 68 | // Focus for select, file, radio, and checkbox 69 | input[type="file"]:focus, 70 | input[type="radio"]:focus, 71 | input[type="checkbox"]:focus { 72 | .tab-focus(); 73 | } 74 | 75 | // Fix for Chrome number input 76 | // Setting certain font-sizes causes the `I` bar to appear on hover of the bottom increment button. 77 | // See https://github.com/twbs/bootstrap/issues/8350 for more. 78 | input[type="number"] { 79 | &::-webkit-outer-spin-button, 80 | &::-webkit-inner-spin-button { 81 | height: auto; 82 | } 83 | } 84 | 85 | // Adjust output element 86 | output { 87 | display: block; 88 | padding-top: (@padding-base-vertical + 1); 89 | font-size: @font-size-base; 90 | line-height: @line-height-base; 91 | color: @input-color; 92 | vertical-align: middle; 93 | } 94 | 95 | // Placeholder 96 | // 97 | // Placeholder text gets special styles because when browsers invalidate entire 98 | // lines if it doesn't understand a selector/ 99 | .form-control { 100 | .placeholder(); 101 | } 102 | 103 | 104 | // Common form controls 105 | // 106 | // Shared size and type resets for form controls. Apply `.form-control` to any 107 | // of the following form controls: 108 | // 109 | // select 110 | // textarea 111 | // input[type="text"] 112 | // input[type="password"] 113 | // input[type="datetime"] 114 | // input[type="datetime-local"] 115 | // input[type="date"] 116 | // input[type="month"] 117 | // input[type="time"] 118 | // input[type="week"] 119 | // input[type="number"] 120 | // input[type="email"] 121 | // input[type="url"] 122 | // input[type="search"] 123 | // input[type="tel"] 124 | // input[type="color"] 125 | 126 | .form-control { 127 | display: block; 128 | width: 100%; 129 | height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border) 130 | padding: @padding-base-vertical @padding-base-horizontal; 131 | font-size: @font-size-base; 132 | line-height: @line-height-base; 133 | color: @input-color; 134 | vertical-align: middle; 135 | background-color: @input-bg; 136 | background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 137 | border: 1px solid @input-border; 138 | border-radius: @input-border-radius; 139 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); 140 | .transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s"); 141 | 142 | // Customize the `:focus` state to imitate native WebKit styles. 143 | .form-control-focus(); 144 | 145 | // Disabled and read-only inputs 146 | // Note: HTML5 says that controls under a fieldset > legend:first-child won't 147 | // be disabled if the fieldset is disabled. Due to implementation difficulty, 148 | // we don't honor that edge case; we style them as disabled anyway. 149 | &[disabled], 150 | &[readonly], 151 | fieldset[disabled] & { 152 | cursor: not-allowed; 153 | background-color: @input-bg-disabled; 154 | } 155 | 156 | // Reset height for `textarea`s 157 | textarea& { 158 | height: auto; 159 | } 160 | } 161 | 162 | 163 | // Form groups 164 | // 165 | // Designed to help with the organization and spacing of vertical forms. For 166 | // horizontal forms, use the predefined grid classes. 167 | 168 | .form-group { 169 | margin-bottom: 15px; 170 | } 171 | 172 | 173 | // Checkboxes and radios 174 | // 175 | // Indent the labels to position radios/checkboxes as hanging controls. 176 | 177 | .radio, 178 | .checkbox { 179 | display: block; 180 | min-height: @line-height-computed; // clear the floating input if there is no label text 181 | margin-top: 10px; 182 | margin-bottom: 10px; 183 | padding-left: 20px; 184 | vertical-align: middle; 185 | label { 186 | display: inline; 187 | margin-bottom: 0; 188 | font-weight: normal; 189 | cursor: pointer; 190 | } 191 | } 192 | .radio input[type="radio"], 193 | .radio-inline input[type="radio"], 194 | .checkbox input[type="checkbox"], 195 | .checkbox-inline input[type="checkbox"] { 196 | float: left; 197 | margin-left: -20px; 198 | } 199 | .radio + .radio, 200 | .checkbox + .checkbox { 201 | margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing 202 | } 203 | 204 | // Radios and checkboxes on same line 205 | .radio-inline, 206 | .checkbox-inline { 207 | display: inline-block; 208 | padding-left: 20px; 209 | margin-bottom: 0; 210 | vertical-align: middle; 211 | font-weight: normal; 212 | cursor: pointer; 213 | } 214 | .radio-inline + .radio-inline, 215 | .checkbox-inline + .checkbox-inline { 216 | margin-top: 0; 217 | margin-left: 10px; // space out consecutive inline controls 218 | } 219 | 220 | // Apply same disabled cursor tweak as for inputs 221 | // 222 | // Note: Neither radios nor checkboxes can be readonly. 223 | input[type="radio"], 224 | input[type="checkbox"], 225 | .radio, 226 | .radio-inline, 227 | .checkbox, 228 | .checkbox-inline { 229 | &[disabled], 230 | fieldset[disabled] & { 231 | cursor: not-allowed; 232 | } 233 | } 234 | 235 | // Form control sizing 236 | .input-sm { 237 | .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small); 238 | } 239 | 240 | .input-lg { 241 | .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large); 242 | } 243 | 244 | 245 | // Form control feedback states 246 | // 247 | // Apply contextual and semantic states to individual form controls. 248 | 249 | // Warning 250 | .has-warning { 251 | .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg); 252 | } 253 | // Error 254 | .has-error { 255 | .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg); 256 | } 257 | // Success 258 | .has-success { 259 | .form-control-validation(@state-success-text; @state-success-text; @state-success-bg); 260 | } 261 | 262 | 263 | // Static form control text 264 | // 265 | // Apply class to a `p` element to make any string of text align with labels in 266 | // a horizontal form layout. 267 | 268 | .form-control-static { 269 | margin-bottom: 0; // Remove default margin from `p` 270 | } 271 | 272 | 273 | // Help text 274 | // 275 | // Apply to any element you wish to create light text for placement immediately 276 | // below a form control. Use for general help, formatting, or instructional text. 277 | 278 | .help-block { 279 | display: block; // account for any element using help-block 280 | margin-top: 5px; 281 | margin-bottom: 10px; 282 | color: lighten(@text-color, 25%); // lighten the text some for contrast 283 | } 284 | 285 | 286 | 287 | // Inline forms 288 | // 289 | // Make forms appear inline(-block) by adding the `.form-inline` class. Inline 290 | // forms begin stacked on extra small (mobile) devices and then go inline when 291 | // viewports reach <768px. 292 | // 293 | // Requires wrapping inputs and labels with `.form-group` for proper display of 294 | // default HTML form controls and our custom form controls (e.g., input groups). 295 | // 296 | // Heads up! This is mixin-ed into `.navbar-form` in navbars.less. 297 | 298 | .form-inline { 299 | 300 | // Kick in the inline 301 | @media (min-width: @screen-sm) { 302 | // Inline-block all the things for "inline" 303 | .form-group { 304 | display: inline-block; 305 | margin-bottom: 0; 306 | vertical-align: middle; 307 | } 308 | 309 | // In navbar-form, allow folks to *not* use `.form-group` 310 | .form-control { 311 | display: inline-block; 312 | } 313 | 314 | // Remove default margin on radios/checkboxes that were used for stacking, and 315 | // then undo the floating of radios and checkboxes to match (which also avoids 316 | // a bug in WebKit: https://github.com/twbs/bootstrap/issues/1969). 317 | .radio, 318 | .checkbox { 319 | display: inline-block; 320 | margin-top: 0; 321 | margin-bottom: 0; 322 | padding-left: 0; 323 | } 324 | .radio input[type="radio"], 325 | .checkbox input[type="checkbox"] { 326 | float: none; 327 | margin-left: 0; 328 | } 329 | } 330 | } 331 | 332 | 333 | // Horizontal forms 334 | // 335 | // Horizontal forms are built on grid classes and allow you to create forms with 336 | // labels on the left and inputs on the right. 337 | 338 | .form-horizontal { 339 | 340 | // Consistent vertical alignment of labels, radios, and checkboxes 341 | .control-label, 342 | .radio, 343 | .checkbox, 344 | .radio-inline, 345 | .checkbox-inline { 346 | margin-top: 0; 347 | margin-bottom: 0; 348 | padding-top: (@padding-base-vertical + 1); // Default padding plus a border 349 | } 350 | 351 | // Make form groups behave like rows 352 | .form-group { 353 | .make-row(); 354 | } 355 | 356 | .form-control-static { 357 | padding-top: (@padding-base-vertical + 1); 358 | } 359 | 360 | // Only right align form labels here when the columns stop stacking 361 | @media (min-width: @screen-sm-min) { 362 | .control-label { 363 | text-align: right; 364 | } 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /Applications/Statistics/Modules/main.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Statistics\Modules; 15 | function main($module, $interface, $date, $start_time, $offset) 16 | { 17 | $err_msg = $notice_msg= ''; 18 | $module = 'WorkerMan'; 19 | $interface = 'Statistics'; 20 | $today = date('Y-m-d'); 21 | $time_now = time(); 22 | multiRequestStAndModules($module, $interface, $date); 23 | $all_st_str = ''; 24 | if(is_array(\Statistics\Lib\Cache::$statisticDataCache['statistic'])) 25 | { 26 | foreach(\Statistics\Lib\Cache::$statisticDataCache['statistic'] as $ip=>$st_str) 27 | { 28 | $all_st_str .= $st_str; 29 | } 30 | } 31 | 32 | $code_map = array(); 33 | $data = formatSt($all_st_str, $date, $code_map); 34 | $interface_name = '整体'; 35 | $success_series_data = $fail_series_data = $success_time_series_data = $fail_time_series_data = array(); 36 | $total_count = $fail_count = 0; 37 | foreach($data as $time_point=>$item) 38 | { 39 | if($item['total_count']) 40 | { 41 | $success_series_data[] = "[".($time_point*1000).",{$item['total_count']}]"; 42 | $total_count += $item['total_count']; 43 | } 44 | $fail_series_data[] = "[".($time_point*1000).",{$item['fail_count']}]"; 45 | $fail_count += $item['fail_count']; 46 | if($item['total_avg_time']) 47 | { 48 | $success_time_series_data[] = "[".($time_point*1000).",{$item['total_avg_time']}]"; 49 | } 50 | $fail_time_series_data[] = "[".($time_point*1000).",{$item['fail_avg_time']}]"; 51 | } 52 | $success_series_data = implode(',', $success_series_data); 53 | $fail_series_data = implode(',', $fail_series_data); 54 | $success_time_series_data = implode(',', $success_time_series_data); 55 | $fail_time_series_data = implode(',', $fail_time_series_data); 56 | 57 | // 总体成功率 58 | $global_rate = $total_count ? round((($total_count - $fail_count)/$total_count)*100, 4) : 100; 59 | // 返回码分布 60 | $code_pie_data = ''; 61 | $code_pie_array = array(); 62 | unset($code_map[0]); 63 | if(empty($code_map)) 64 | { 65 | $code_map[0] = $total_count > 0 ? $total_count : 1; 66 | } 67 | if(is_array($code_map)) 68 | { 69 | $total_item_count = array_sum($code_map); 70 | foreach($code_map as $code=>$count) 71 | { 72 | $code_pie_array[] = "[\"$code:{$count}个\", ".round($count*100/$total_item_count, 4)."]"; 73 | } 74 | $code_pie_data = implode(',', $code_pie_array); 75 | } 76 | 77 | unset($_GET['start_time'], $_GET['end_time'], $_GET['date'], $_GET['fn']); 78 | $query = http_build_query($_GET); 79 | 80 | // 删除末尾0的记录 81 | if($today == $date) 82 | { 83 | while(!empty($data) && ($item = end($data)) && $item['total_count'] == 0 && ($key = key($data)) && $time_now < $key) 84 | { 85 | unset($data[$key]); 86 | } 87 | } 88 | 89 | $table_data = $html_class = ''; 90 | if($data) 91 | { 92 | $first_line = true; 93 | foreach($data as $item) 94 | { 95 | if($first_line) 96 | { 97 | $first_line = false; 98 | } 99 | if($item['total_count'] == 0) 100 | { 101 | continue; 102 | } 103 | $html_class = 'class="danger"'; 104 | if($item['total_count'] == 0) 105 | { 106 | $html_class = ''; 107 | } 108 | elseif($item['precent']>=99.99) 109 | { 110 | $html_class = 'class="success"'; 111 | } 112 | elseif($item['precent']>=99) 113 | { 114 | $html_class = ''; 115 | } 116 | elseif($item['precent']>=98) 117 | { 118 | $html_class = 'class="warning"'; 119 | } 120 | $table_data .= "\n 121 | {$item['time']} 122 | {$item['total_count']} 123 | {$item['total_avg_time']} 124 | {$item['suc_count']} 125 | {$item['suc_avg_time']} 126 | ".($item['fail_count']>0?("{$item['fail_count']}"):$item['fail_count'])." 127 | {$item['fail_avg_time']} 128 | {$item['precent']}% 129 | 130 | "; 131 | } 132 | } 133 | 134 | // date btn 135 | $date_btn_str = ''; 136 | for($i=13;$i>=1;$i--) 137 | { 138 | $the_time = strtotime("-$i day"); 139 | $the_date = date('Y-m-d',$the_time); 140 | $html_the_date = $date == $the_date ? "$the_date" : $the_date; 141 | $date_btn_str .= ''.$html_the_date.''; 142 | if($i == 7) 143 | { 144 | $date_btn_str .= '
      '; 145 | } 146 | } 147 | $the_date = date('Y-m-d'); 148 | $html_the_date = $date == $the_date ? "$the_date" : $the_date; 149 | $date_btn_str .= ''.$html_the_date.''; 150 | 151 | if( \Statistics\Lib\Cache::$lastFailedIpArray) 152 | { 153 | $err_msg = '无法从以下数据源获取数据:'; 154 | foreach (\Statistics\Lib\Cache::$lastFailedIpArray as $ip) 155 | { 156 | $err_msg .= $ip.'::'.\Statistics\Config::$ProviderPort . ' '; 157 | } 158 | } 159 | 160 | if(empty(\Statistics\Lib\Cache::$ServerIpList)) 161 | { 162 | $notice_msg = <<数据源为空 164 | 您可以 探测数据源或者添加数据源 165 | EOT; 166 | } 167 | 168 | include ST_ROOT . '/Views/header.tpl.php'; 169 | include ST_ROOT . '/Views/main.tpl.php'; 170 | include ST_ROOT . '/Views/footer.tpl.php'; 171 | } 172 | 173 | function multiRequestStAndModules($module, $interface, $date) 174 | { 175 | \Statistics\Lib\Cache::$statisticDataCache['statistic'] = array(); 176 | $buffer = json_encode(array('cmd'=>'get_statistic','module'=>$module, 'interface'=>$interface, 'date'=>$date))."\n"; 177 | $ip_list = (!empty($_GET['ip']) && is_array($_GET['ip'])) ? $_GET['ip'] : \Statistics\Lib\Cache::$ServerIpList; 178 | $reqest_buffer_array = array(); 179 | $port = \Statistics\Config::$ProviderPort;; 180 | foreach($ip_list as $ip) 181 | { 182 | $reqest_buffer_array["$ip:$port"] = $buffer; 183 | } 184 | $read_buffer_array = multiRequest($reqest_buffer_array); 185 | foreach($read_buffer_array as $address => $buf) 186 | { 187 | list($ip, $port) = explode(':',$address); 188 | $body_data = json_decode(trim($buf), true); 189 | $statistic_data = isset($body_data['statistic']) ? $body_data['statistic'] : ''; 190 | $modules_data = isset($body_data['modules']) ? $body_data['modules'] : array(); 191 | // 整理modules 192 | foreach($modules_data as $mod => $interfaces) 193 | { 194 | if(!isset(\Statistics\Lib\Cache::$modulesDataCache[$mod])) 195 | { 196 | \Statistics\Lib\Cache::$modulesDataCache[$mod] = array(); 197 | } 198 | foreach($interfaces as $if) 199 | { 200 | \Statistics\Lib\Cache::$modulesDataCache[$mod][$if] = $if; 201 | } 202 | } 203 | \Statistics\Lib\Cache::$statisticDataCache['statistic'][$ip] = $statistic_data; 204 | } 205 | } 206 | 207 | function formatSt($str, $date, &$code_map) 208 | { 209 | // time:[suc_count:xx,suc_cost_time:xx,fail_count:xx,fail_cost_time:xx] 210 | $st_data = $code_map = array(); 211 | $st_explode = explode("\n", $str); 212 | // 汇总计算 213 | foreach($st_explode as $line) 214 | { 215 | // line = IP time suc_count suc_cost_time fail_count fail_cost_time code_json 216 | $line_data = explode("\t", $line); 217 | if(!isset($line_data[5])) 218 | { 219 | continue; 220 | } 221 | $time_line = $line_data[1]; 222 | $time_line = ceil($time_line/300)*300; 223 | $suc_count = $line_data[2]; 224 | $suc_cost_time = $line_data[3]; 225 | $fail_count = $line_data[4]; 226 | $fail_cost_time = $line_data[5]; 227 | $tmp_code_map = json_decode($line_data[6], true); 228 | if(!isset($st_data[$time_line])) 229 | { 230 | $st_data[$time_line] = array('suc_count'=>0, 'suc_cost_time'=>0, 'fail_count'=>0, 'fail_cost_time'=>0); 231 | } 232 | $st_data[$time_line]['suc_count'] += $suc_count; 233 | $st_data[$time_line]['suc_cost_time'] += $suc_cost_time; 234 | $st_data[$time_line]['fail_count'] += $fail_count; 235 | $st_data[$time_line]['fail_cost_time'] += $fail_cost_time; 236 | 237 | if(is_array($tmp_code_map)) 238 | { 239 | foreach($tmp_code_map as $code=>$count) 240 | { 241 | if(!isset($code_map[$code])) 242 | { 243 | $code_map[$code] = 0; 244 | } 245 | $code_map[$code] += $count; 246 | } 247 | } 248 | } 249 | // 按照时间排序 250 | ksort($st_data); 251 | // time => [total_count:xx,suc_count:xx,suc_avg_time:xx,fail_count:xx,fail_avg_time:xx,percent:xx] 252 | $data = array(); 253 | // 计算成功率 耗时 254 | foreach($st_data as $time_line=>$item) 255 | { 256 | $data[$time_line] = array( 257 | 'time' => date('Y-m-d H:i:s', $time_line), 258 | 'total_count' => $item['suc_count']+$item['fail_count'], 259 | 'total_avg_time'=> $item['suc_count']+$item['fail_count'] == 0 ? 0 : number_format(($item['suc_cost_time']+$item['fail_cost_time'])/($item['suc_count']+$item['fail_count']), 6), 260 | 'suc_count' => $item['suc_count'], 261 | 'suc_avg_time' => $item['suc_count'] == 0 ? $item['suc_count'] : number_format($item['suc_cost_time']/$item['suc_count'], 6), 262 | 'fail_count' => $item['fail_count'], 263 | 'fail_avg_time' => $item['fail_count'] == 0 ? 0 : number_format($item['fail_cost_time']/$item['fail_count'], 6), 264 | 'precent' => $item['suc_count']+$item['fail_count'] == 0 ? 0 : number_format(($item['suc_count']*100/($item['suc_count']+$item['fail_count'])), 4), 265 | ); 266 | } 267 | $time_point = strtotime($date); 268 | for($i=0;$i<288;$i++) 269 | { 270 | $data[$time_point] = isset($data[$time_point]) ? $data[$time_point] : 271 | array( 272 | 'time' => date('Y-m-d H:i:s', $time_point), 273 | 'total_count' => 0, 274 | 'total_avg_time'=> 0, 275 | 'suc_count' => 0, 276 | 'suc_avg_time' => 0, 277 | 'fail_count' => 0, 278 | 'fail_avg_time' => 0, 279 | 'precent' => 0, 280 | ); 281 | $time_point +=300; 282 | } 283 | ksort($data); 284 | return $data; 285 | } 286 | --------------------------------------------------------------------------------