You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by zh...@apache.org on 2015/07/02 13:55:06 UTC
[1/4] incubator-kylin git commit: KYLIN-792 ,add performance module
Repository: incubator-kylin
Updated Branches:
refs/heads/0.7-staging cbcfd59df -> a935c7bae
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/app/index.html
----------------------------------------------------------------------
diff --git a/webapp/app/index.html b/webapp/app/index.html
index 6824498..f1087ed 100644
--- a/webapp/app/index.html
+++ b/webapp/app/index.html
@@ -30,20 +30,21 @@
<!-- ref:css css/styles.min.<%= buildNumber %>.css -->
<link rel="stylesheet" type="text/css" href="components/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="components/font-awesome/css/font-awesome.css">
-
+ <link rel="stylesheet" type="text/css" href="css/AdminLTE-fonts.css">
+ <link rel="stylesheet" type="text/css" href="components/ng-grid/ng-grid.css">
<link rel="stylesheet" type="text/css" href="components/angular-tree-control/css/tree-control.css">
<link rel="stylesheet" type="text/css" href="components/angular-tree-control/css/tree-control-attribute.css">
+ <link rel="stylesheet" type="text/css" href="components/animate.css/animate.css">
+
<link rel="stylesheet" type="text/css" href="components/messenger/build/css/messenger.css">
<link rel="stylesheet" type="text/css" href="components/messenger/build/css/messenger-theme-ice.css">
- <link rel="stylesheet" type="text/css" href="components/ng-grid/ng-grid.css">
<link rel="stylesheet" type="text/css" href="components/chosen/chosen.css">
<link rel="stylesheet" type="text/css" href="components/angular-chosen-localytics/chosen-spinner.css">
- <link rel="stylesheet" type="text/css" href="components/animate.css/animate.css">
<link rel="stylesheet" type="text/css" href="components/nvd3/nv.d3.min.css">
-
- <link rel="stylesheet" type="text/css" href="css/AdminLTE-fonts.css">
<link rel="stylesheet" type="text/css" href="css/AdminLTE.css">
<link rel="stylesheet" type="text/css" href="components/bootstrap-sweetalert/lib/sweet-alert.css">
+ <link rel="stylesheet" type="text/css" href="components/angular-busy/dist/angular-busy.css">
+
<link rel="stylesheet/less" href="less/build.less">
<!-- endref -->
@@ -93,7 +94,7 @@
<script src="components/moment/moment.js"></script>
<script src="components/d3/d3.min.js"></script>
-<script src="components/nvd3/nv.d3.min.js"></script>
+<script src="components/nvd3/nv.d3.js"></script>
<script src="components/angularjs-nvd3-directives/dist/angularjs-nvd3-directives.js"></script>
<script src="components/bootstrap-sweetalert/lib/sweet-alert.js"></script>
<script src="components/angular-sweetalert/SweetAlert.js"></script>
@@ -101,6 +102,7 @@
<script src="components/angular-underscore/angular-underscore.js"></script>
<script src="components/jquery-ui/jquery-ui.min.js"></script>
<script src="components/angular-ui-sortable/sortable.js"></script>
+<script src="components/angular-busy/dist/angular-busy.js"></script>
<script src="js/app.js"></script>
<script src="js/config.js"></script>
@@ -127,6 +129,7 @@
<script src="js/services/tree.js"></script>
<script src="js/services/users.js"></script>
<script src="js/services/ngLoading.js"></script>
+<script src="js/services/dashboard.js"></script>
<script src="js/model/cubeConfig.js"></script>
<script src="js/model/jobConfig.js"></script>
@@ -157,6 +160,7 @@
<script src="js/controllers/cubeFilter.js"></script>
<script src="js/controllers/cubeRefresh.js"></script>
<script src="js/controllers/cubeAdvanceSetting.js"></script>
+<script src="js/controllers/dashboard.js"></script>
<!-- endref -->
<!-- ref:remove -->
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/app/js/app.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/app.js b/webapp/app/js/app.js
index 59d679e..ba288cb 100644
--- a/webapp/app/js/app.js
+++ b/webapp/app/js/app.js
@@ -17,4 +17,4 @@
*/
//Kylin Application Module
-KylinApp = angular.module('kylin', ['ngRoute', 'ngResource', 'ngGrid', 'ui.bootstrap', 'ui.ace', 'base64', 'angularLocalStorage', 'localytics.directives', 'treeControl', 'nvd3ChartDirectives', 'ngLoadingRequest', 'oitozero.ngSweetAlert', 'ngCookies', 'angular-underscore', 'ngAnimate', 'ui.sortable']);
+KylinApp = angular.module('kylin', ['ngRoute', 'ngResource', 'ngGrid', 'ui.bootstrap', 'ui.ace', 'base64', 'angularLocalStorage', 'localytics.directives', 'treeControl', 'nvd3ChartDirectives', 'ngLoadingRequest', 'oitozero.ngSweetAlert', 'ngCookies', 'angular-underscore', 'ngAnimate', 'ui.sortable',,'cgBusy']);
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/app/js/controllers/dashboard.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/dashboard.js b/webapp/app/js/controllers/dashboard.js
new file mode 100644
index 0000000..40b7b8e
--- /dev/null
+++ b/webapp/app/js/controllers/dashboard.js
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+KylinApp.controller('DashBoardCtrl', function ($scope, DashBoardService, $log, $q) {
+
+
+ $scope.stastic = {
+ countUser: 0,
+ last30DayPercentile: 0,
+ cubeStorage: 0,
+ avgDayQuery: 0,
+ cubesCount: 0
+ }
+
+ $scope.eachDayPercentileData=[];
+
+
+ $scope.reduceCubeSourceTicks=false;
+
+ // each day percentile chart
+ $scope.cubeInfo = function () {
+ var cubeSourceRecords ={"key":"Cube Source Records","values":[],"sizes":[]};
+ $scope.cubeInfoPromise = DashBoardService.listCubes({},
+ function (data) {
+ if(data.length>30){
+ $scope.reduceCubeSourceTicks=true;
+ }
+ for (var i = 0; i < data.length; i++) {
+ cubeSourceRecords.values.push([data[i].name,parseInt(data[i].input_records_count)]);
+ cubeSourceRecords.sizes.push((data[i].size_kb));
+ $log.info(data[i]);
+ }
+ $scope.cubeUsageData = [cubeSourceRecords];
+
+ }, function (result) {
+ $log.error(result);
+ }).$promise;
+ }();
+
+
+ $scope.cubeSourceYAxisTickFormat = function(){
+ return d3.format('0');
+ }
+
+ $scope.avgDayQuery = function () {
+ $scope.avgDayQueryPromise = DashBoardService.avgDayQuery({},
+ function (data) {
+ if (!isNaN(data[0][0])) {
+ $scope.stastic.avgDayQuery = Math.round(data[0][0]);
+ } else {
+ $log.info("No data available.");
+ }
+ $log.info("avg day query:" + data);
+ }, function (result) {
+ $log.error(result);
+ }).$promise;
+ }();
+
+ $scope.totalQueryUser = function () {
+ $scope.queryUserPromise = DashBoardService.totalQueryUser({},
+ function (data) {
+ if (!isNaN(data[0][0])) {
+ $scope.stastic.userCount = data[0][0];
+ } else {
+ $log.info("No data available.");
+ }
+
+ $log.info("total query user:" + data);
+ }, function (result) {
+ $log.error(result);
+ }).$promise;
+ }();
+
+ $scope.last30DayPercentile = function () {
+ $scope.last30DayPercentilePromise = DashBoardService.last30DayPercentile({},
+ function (data) {
+ if (!isNaN(data[0][0])) {
+ $scope.stastic.last30DayPercentile = Math.round(data[0][0] * 100) / 100;
+ } else {
+ $log.info("No data available.");
+ }
+ $log.info("last 30 Day 90th Percentile:" + data);
+ }, function (result) {
+ $log.error(result);
+ }).$promise;
+ }();
+
+
+ //daily query num
+ $scope.dailyQueryData = [];
+
+
+ // last 30 days
+ $scope.dailyQueryCount = function () {
+ var queryCount ={"key":"Query Count","bar":true,"values":[]};
+ $scope.dailyQueryCountPromise = DashBoardService.dailyQueryCount({},
+ function (data) {
+ for (var i = 0; i < data.length; i++) {
+ $scope.dailyQueryData.push(parseInt(data[i][1]));
+ queryCount.values.push([new Date(data[i][0]).getTime()/1000,data[i][1]]);
+ }
+ console.log("daily query count");
+ $scope.eachDayPercentileData.push(queryCount);
+ }, function (result) {
+ $log.error(result);
+ }).$promise;
+ };
+
+ $scope.eachDayPercentile = function () {
+ var percenTile90 ={"key":"90%-ile","values":[]},percenTile95 = {"key":"95%-ile","values":[]},queryCount ={"key":"Query Count","bar":true,"values":[]};;
+ $scope.eachDayPercentilePromise = DashBoardService.eachDayPercentile({},
+ function (data) {
+ for (var i = 0; i < data.length; i++) {
+ var _latency90 = data[i][1].split(",")[0].substr(1);
+ var _latency95 = data[i][1].split(",")[1].substr(0, data[i][1].length - 1);
+ var _querycount = data[i][2];
+ percenTile90.values.push([new Date(data[i][0]).getTime()/1000,Math.round(parseFloat(_latency90) * 100) / 100]);
+ percenTile95.values.push([new Date(data[i][0]).getTime()/1000,Math.round(parseFloat(_latency95) * 100) / 100]);
+ queryCount.values.push([new Date(data[i][0]).getTime()/1000,Math.round(parseInt(_querycount) * 100) / 100]);
+ }
+ $scope.eachDayPercentileData=[percenTile90,percenTile95,queryCount];
+ }, function (result) {
+ $log.error(result);
+ }).$promise;
+ }();
+
+
+ $scope.dailyLatencyReportRightYaxis = function(){
+ return "Query Latency";
+ }
+
+ $scope.xAxisTickFormatFunction = function(){
+ return function(d){
+ return d3.time.format('%Y-%m-%d')(moment.unix(d).toDate());
+ }
+ };
+
+
+ $scope.legendColorFunction = function(){
+ return function(d){
+ return '#E01B5D';
+ }
+ };
+
+
+ $scope.reduceProjectPercentileTicks=false;
+
+ $scope.projectPercentile = function () {
+
+ var percenTile90 ={"key":"90%-ile","values":[]},percenTile95 = {"key":"95%-ile","values":[]};
+
+
+ $scope.projectPercentilePromise = DashBoardService.projectPercentile({},
+ function (data) {
+ if(data.length>30){
+ $scope.reduceProjectPercentileTicks=true;
+ }
+ for (var i = 0; i < data.length; i++) {
+
+ var _latency90 = data[i][1].split(",")[0].substr(1);
+ var _latency95 = data[i][1].split(",")[1].substr(0, data[i][1].length - 1);
+ percenTile90.values.push([data[i][0],Math.round(parseFloat(_latency90) * 100) / 100]);
+ percenTile95.values.push([data[i][0],Math.round(parseFloat(_latency95) * 100) / 100]);
+
+ }
+ $scope.eachProjectPercentileData=[percenTile90,percenTile95];
+ }, function (result) {
+ $log.error(result);
+ }).$promise;
+ }();
+
+ $scope.projectPercentileToolTipContentFunction = function(key, x, y, e, graph) {
+ return '<table>'+'<tr>'+'<td>'+'<strong>Latency</strong>'+'</td>'+'<td>'+' : '+y+'(S)'+'</td>'+'</tr>'+'<tr>'+'<td>'+'<strong>Percentile</strong>'+'</td>'+'<td>'+' : '+key+'</td>'+'</tr>'+'<tr>'+'<td>'+'<strong>Project</strong>'+'</td>'+'<td>'+' : '+x+'</td>'+'</tr>'+'</table>';
+
+ }
+
+ $scope.dailyPercentileToolTip = function(key, x, y, e, graph) {
+ var suffix ='';
+ if(key.indexOf('ile')!=-1){
+ suffix = '(S)';
+ }
+ return '<table>'+'<tr>'+'<td>'+'<strong>'+key+'</strong>'+'</td>'+'<td>'+' : '+y+suffix+'</td>'+'</tr>'+'<tr>'+'<td>'+'<strong>Date</strong>'+'</td>'+'<td>'+' : '+x+'</td>'+'</tr>'+'</table>';
+
+ }
+ $scope.cubeToolTipContentFunction = function(key, x, y, e, graph) {
+ return '<table>'+'<tr>'+'<td>'+'<strong>Source Records</strong>'+'</td>'+'<td>'+' : '+parseInt(y)+'</td>'+'</tr>'+'<tr>'+'<td>'+'<strong>Source Size</strong>'+'</td>'+'<td>'+' : '+$scope.dataSize(e.series.sizes[e.pointIndex]*1024)+'</td>'+'</tr>'+'<tr>'+'<td>'+'<strong>Cube Name</strong>'+'</td>'+'<td>'+' : '+x+'</td>'+'</tr>'+'</table>';
+ }
+
+ $scope.commonToolTipContentFunction = function(key, x, y, e, graph) {
+ return '<p>' + x +':'+y+ '</p>';
+ }
+
+ $scope.cubesStorage = function () {
+ $scope.cubesStoragePromise = DashBoardService.cubesStorage({},
+ function (cubes) {
+ $scope.stastic.cubesCount = cubes.length;
+ var _cubeStorage = $scope.getTotalSize(cubes);
+ $scope.stastic.cubeStorage = _cubeStorage;
+ }, function (result) {
+ $log.error(result);
+ }).$promise;
+ }();
+
+ $scope.getTotalSize = function (cubes) {
+ var size = 0;
+ if (!cubes) {
+ return 0;
+ }
+ else {
+ for (var i = 0; i < cubes.length; i++) {
+ size += cubes[i].size_kb;
+ }
+ return $scope.dataSize(size * 1024);
+ }
+ };
+
+});
+
+
+
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/app/js/services/dashboard.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/services/dashboard.js b/webapp/app/js/services/dashboard.js
new file mode 100644
index 0000000..f5b1454
--- /dev/null
+++ b/webapp/app/js/services/dashboard.js
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+KylinApp.factory('DashBoardService', ['$resource', function ($resource, config) {
+ return $resource(Config.service.url + 'performance/:action', {}, {
+// avgCubeLatency: {method: 'GET', params: {action: 'avgCubeLatency'}, isArray: true},
+ avgDayQuery: {method: 'GET', params: {action: 'avgDayQuery'}, isArray: true},
+ dailyQueryCount: {method: 'GET', params: {action: 'dailyQueryCount'}, isArray: true},
+ eachDayPercentile: {method: 'GET', params: {action: 'eachDayPercentile'}, isArray: true}, // last 30 day 90,95 percentile for each day
+ projectPercentile: {method: 'GET', params: {action: 'projectPercentile'}, isArray: true}, // last 30 day 90,95 percentile for each project
+ cubesStorage: {method: 'GET', params: {action: 'cubesStorage'}, isArray: true},
+ last30DayPercentile: {method: 'GET', params: {action: 'last30DayPercentile'}, isArray: true},//90th percentile of last 30 day
+ totalQueryUser: {method: 'GET', params: {action: 'totalQueryUser'}, isArray: true},
+ listCubes: {method: 'GET', params: {action: 'listCubes'}, isArray: true}
+ });
+}])
+;
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/app/js/services/users.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/services/users.js b/webapp/app/js/services/users.js
index d05ca94..891ad89 100644
--- a/webapp/app/js/services/users.js
+++ b/webapp/app/js/services/users.js
@@ -49,9 +49,16 @@ KylinApp.service('UserService', function ($http, $q) {
var homePage = "/login";
if (curUser.userDetails && curUser.userDetails.authorities) {
- angular.forEach(curUser.userDetails.authorities, function (authority, index) {
- homePage = (!!roles[authority.authority]) ? roles[authority.authority] : homePage;
- });
+ var authorities = curUser.userDetails.authorities;
+ for(var i=0;i<authorities.length;i++){
+ if(authorities[i].authority==="ROLE_ADMIN"){
+ homePage = roles[authorities[i].authority];
+ break;
+ }else{
+ homePage = roles[authorities[i].authority]
+ }
+ }
+
}
return homePage;
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/app/less/app.less
----------------------------------------------------------------------
diff --git a/webapp/app/less/app.less b/webapp/app/less/app.less
index 162c929..4edf83c 100644
--- a/webapp/app/less/app.less
+++ b/webapp/app/less/app.less
@@ -605,3 +605,20 @@ ul.messenger .messenger-message-inner ,.ngCellText{
max-height: 300px;
overflow-y: auto;
}
+
+.dashboard_wrapper{
+ min-height: 100%;
+ background-color: #ecf0f5;
+ z-index: 800;
+}
+
+
+.size-h1{font-size:36px}
+.size-h2{font-size:30px}
+.size-h3{font-size:24px}
+.size-h4{font-size:18px}
+
+.text-muted {
+ color: #777;
+}
+.tick line {display: none;}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/app/partials/admin/admin.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/admin/admin.html b/webapp/app/partials/admin/admin.html
index c17aba4..c7b23f0 100644
--- a/webapp/app/partials/admin/admin.html
+++ b/webapp/app/partials/admin/admin.html
@@ -71,6 +71,9 @@
<a class="label-lg label-yellow arrowed-right"style="font-size:18px;" tooltip="Cluster Resource Monitoring" href="{{config.reference_links['hadoop'].link}}" >
Hadoop Monitor
</a>
+ <a href="dashboard">
+ Dashboard
+ </a>
</div>
</div>
</div>
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/app/partials/dashboard.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/dashboard.html b/webapp/app/partials/dashboard.html
new file mode 100644
index 0000000..acccfe4
--- /dev/null
+++ b/webapp/app/partials/dashboard.html
@@ -0,0 +1,228 @@
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+-->
+<div class="wrapper">
+ <aside class="main-sidebar" style="padding-top:0px !important;">
+ <!-- sidebar: style can be found in sidebar.less -->
+ <section class="sidebar" style="height: auto;">
+
+ </section>
+ <!-- /.sidebar -->
+ </aside>
+
+ <div class="content-wrapper" style="min-height: 800px;">
+
+ <section class="content-header">
+ <h1>
+ Dashboard
+ <small></small>
+ </h1>
+ </section>
+
+ <section class="content">
+ <!-- Info boxes -->
+ <div class="row">
+ <div class="col-md-3 col-sm-6 col-xs-12">
+ <div class="info-box">
+ <span class="info-box-icon bg-aqua"><i class="fa fa-users"></i></span>
+
+ <div class="info-box-content" cg-busy="{promise:queryUserPromise,message:'Loading...'}">
+ <span class="info-box-number size-h2 text-muted">{{stastic.userCount}}</span>
+ <span class="info-box-text size-h4 text-muted">Users</span>
+ </div>
+ <!-- /.info-box-content -->
+ </div>
+ <!-- /.info-box -->
+ </div>
+ <!-- /.col -->
+ <div class="col-md-3 col-sm-6 col-xs-12">
+ <div class="info-box">
+ <span class="info-box-icon bg-red"><i class="fa fa-eye"></i></span>
+
+ <div class="info-box-content" cg-busy="{promise:last30DayPercentilePromise,message:'Loading...'}">
+ <span class="info-box-number size-h2 text-muted">{{stastic.last30DayPercentile}} S</span>
+ <span class="info-box-text size-h4 text-muted">90%-ile Latency (last 30 days)</span>
+ </div>
+ <!-- /.info-box-content -->
+ </div>
+ <!-- /.info-box -->
+ </div>
+ <!-- /.col -->
+
+ <div class="col-md-3 col-sm-6 col-xs-12">
+ <div class="info-box">
+ <span class="info-box-icon bg-green"><i class="fa fa-database"></i></span>
+
+ <div class="info-box-content" cg-busy="{promise:cubesStoragePromise,message:'Loading...'}">
+ <span class="info-box-number size-h2 text-muted">{{stastic.cubeStorage}}</span>
+ <span class="info-box-text size-h4 text-muted">Storage</span>
+ </div>
+ <!-- /.info-box-content -->
+ </div>
+ <!-- /.info-box -->
+ </div>
+ <!-- /.col -->
+ <div class="col-md-3 col-sm-6 col-xs-12">
+ <div class="info-box">
+ <span class="info-box-icon bg-yellow"><i class="fa fa-cubes"></i></span>
+
+ <div class="info-box-content" cg-busy="{promise:cubesStoragePromise,message:'Loading...'}">
+ <span class="info-box-number size-h2 text-muted">{{stastic.cubesCount}}</span>
+ <span class="info-box-text size-h4 text-muted">Cubes</span>
+ </div>
+ <!-- /.info-box-content -->
+ </div>
+ <!-- /.info-box -->
+ </div>
+ <!-- /.col -->
+ </div>
+ <!-- /.row -->
+
+ <div class="row" ng-controller="DashBoardCtrl">
+ <div class="col-md-12">
+ <div class="box">
+ <div class="box-header with-border">
+ <h3 class="box-title">Query Latency (last 30 days)</h3>
+ </div>
+ <!-- /.box-header -->
+ <div class="box-body">
+
+
+ <div class="row">
+ <div class="col-md-12">
+ <p class="text-center">
+ <strong></strong>
+ </p>
+
+
+ <!-- Sales Chart Canvas -->
+ <!--<div cg-busy="{promise:queryCountDailyPercentilePromise,message:'Loading...'}">-->
+ <div cg-busy="[eachDayPercentilePromise]">
+
+ <nvd3-line-plus-bar-chart
+ data="eachDayPercentileData"
+ objectequality="true"
+ margin="{left:100,top:10,bottom:20,right:100}"
+ showXAxis="true"
+ showYAxis="true"
+ y1AxisLabel="Query Count"
+ y2AxisLabel="Query Latency(seconds)"
+ forceY="[0]"
+ height="400"
+ xaxisrotatelabels="-45"
+ tooltips="true"
+ useInteractiveGuideline="true"
+ showLegend="true"
+ legendWidth="200"
+ legendHeight="100"
+ xAxisTickFormat="xAxisTickFormatFunction()"
+ interactive="true"
+ tooltipcontent="dailyPercentileToolTip"
+ >
+ <svg></svg>
+ </nvd3-line-plus-bar-chart>
+ </div>
+ <!-- /.chart-responsive -->
+ </div>
+ <!-- /.col -->
+ </div>
+ <!-- /.row -->
+
+ <div class="row">
+ <div class="col-md-6">
+ <div class="box">
+ <div class="box-header with-border">
+ <h3 class="box-title">Cube Source</h3>
+ </div>
+ <div class="box-body">
+ <!-- Sales Chart Canvas -->
+ <div cg-busy="{promise:cubeInfoPromise}">
+ <nvd3-multi-bar-chart
+ margin="{left:100,top:10,bottom:100,right:60}"
+ data="cubeUsageData"
+ objectequality="true"
+ height="350"
+ showXAxis="true"
+ reducexticks="{{reduceCubeSourceTicks}}"
+ xaxisrotatelabels="-45"
+ showYAxis="true"
+ forceY="[0]"
+ xAxisLabel="Cube Name"
+ yAxisLabel="Cube Source Records"
+ showLegend="true"
+ tooltips="true"
+ legendWidth="200"
+ legendHeight="100"
+ tooltips="true"
+ yAxisTickFormat="cubeSourceYAxisTickFormat()"
+ tooltipcontent="cubeToolTipContentFunction"
+ >
+ <svg></svg>
+ </nvd3-multi-bar-chart>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="col-md-6">
+ <div class="box">
+ <div class="box-header with-border">
+ <h3 class="box-title">Project Latency (last 30 days)</h3>
+ </div>
+ <div class="box-body">
+ <!-- Sales Chart Canvas -->
+ <div cg-busy="{promise:projectPercentilePromise}">
+ <nvd3-multi-bar-chart
+ margin="{left:100,top:10,bottom:100,right:60}"
+ data="eachProjectPercentileData"
+ objectequality="true"
+ height="350"
+ showXAxis="true"
+ xAxisLabel="Project Name"
+ reducexticks="{{reduceProjectPercentileTicks}}"
+ xaxisrotatelabels="-45"
+ showYAxis="true"
+ yAxisLabel="Query Latency(seconds)"
+ showLegend="true"
+ legendWidth="200"
+ legendHeight="100"
+ tooltips="true"
+ tooltipcontent="projectPercentileToolTipContentFunction"
+ >
+ <svg></svg>
+ </nvd3-multi-bar-chart>
+
+ </div>
+ </div>
+ </div>
+
+
+ </div>
+ </div>
+
+
+ </div>
+ <!-- ./box-body -->
+ </div>
+ <!-- /.box -->
+ </div>
+ <!-- /.col -->
+ </div>
+ <!-- /.row -->
+ </section>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/app/partials/header.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/header.html b/webapp/app/partials/header.html
index eb94664..74e03a0 100644
--- a/webapp/app/partials/header.html
+++ b/webapp/app/partials/header.html
@@ -26,7 +26,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
- <a class="navbar-brand" style="padding: 2px 10px 0px 0px"><img src="image/logo.png" height="40px" width="40px"/><small> Kylin </small></a>
+ <a class="navbar-brand" style="padding: 2px 10px 0px 0px;"><img src="image/logo.png" height="40px" width="40px"/><small> Kylin </small></a>
</div>
<div class="navbar-collapse collapse" collapse="isCollapsed">
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/app/routes.json
----------------------------------------------------------------------
diff --git a/webapp/app/routes.json b/webapp/app/routes.json
index 9f13c70..6401528 100644
--- a/webapp/app/routes.json
+++ b/webapp/app/routes.json
@@ -1,5 +1,12 @@
[
{
+ "url": "/dashboard",
+ "params": {
+ "templateUrl": "partials/dashboard.html",
+ "controller": "DashBoardCtrl"
+ }
+ },
+ {
"url": "/cubes",
"params": {
"templateUrl": "partials/cubes/cubes.html",
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/bower.json
----------------------------------------------------------------------
diff --git a/webapp/bower.json b/webapp/bower.json
index 298cba3..f9d99fb 100755
--- a/webapp/bower.json
+++ b/webapp/bower.json
@@ -19,7 +19,7 @@
"moment": "2.5.1",
"d3": "3.4.4",
"nvd3": "1.1.15-beta",
- "angularjs-nvd3-directives": "0.0.5-beta",
+ "angularjs-nvd3-directives": "0.0.7",
"angular-sweetalert": "~1.0.3",
"bootstrap-sweetalert": "~0.4.3",
"angular-underscore": "~0.5.0",
@@ -27,8 +27,8 @@
"underscore": "~1.7.0",
"fuelux": "~3.5.1",
"angular-animate": "1.2",
- "angular-cookies":"1.2"
-
+ "angular-cookies": "1.2",
+ "angular-busy": "~4.0"
},
"devDependencies": {
"less.js": "~1.4.0",
@@ -39,9 +39,9 @@
"nvd3": "1.1.15-beta",
"d3": "3.4.4",
"moment": "2.4.0",
+ "angular-busy": "~4.0",
"angular-resource": "1.2.15",
- "angularLocalStorage": "0.1.7",
"angular-cookies": "~1.2.0-rc.2",
- "angular-animate": "1.2"
+ "angular-animate": "~1.2"
}
}
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/grunt.json
----------------------------------------------------------------------
diff --git a/webapp/grunt.json b/webapp/grunt.json
index 9a4499f..fa272c9 100755
--- a/webapp/grunt.json
+++ b/webapp/grunt.json
@@ -40,6 +40,7 @@
"app/components/angular-underscore/angular-underscore.js",
"app/components/jquery-ui/jquery-ui.min.js",
"app/components/angular-ui-sortable/sortable.js",
+ "app/components/angular-busy/dist/angular-busy.js",
"tmp/js/scripts.js"
],
"dest": "tmp/js/scripts.min.js"
@@ -60,6 +61,7 @@
"app/components/nvd3/nv.d3.min.css",
"app/css/AdminLTE.css",
"app/components/bootstrap-sweetalert/lib/sweet-alert.css",
+ "app/components/angular-busy/dist/angular-busy.css",
"tmp/css/styles.css"
],
"dest": "tmp/css/styles.min.css"
[4/4] incubator-kylin git commit: KYLIN-792 ,add performance module
Posted by zh...@apache.org.
KYLIN-792 ,add performance module
Project: http://git-wip-us.apache.org/repos/asf/incubator-kylin/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-kylin/commit/a935c7ba
Tree: http://git-wip-us.apache.org/repos/asf/incubator-kylin/tree/a935c7ba
Diff: http://git-wip-us.apache.org/repos/asf/incubator-kylin/diff/a935c7ba
Branch: refs/heads/0.7-staging
Commit: a935c7bae805b2e93736e8f7cb93dc0f0e6f467d
Parents: cbcfd59
Author: jiazhong <ji...@ebay.com>
Authored: Wed May 27 23:01:10 2015 +0800
Committer: jiazhong <ji...@ebay.com>
Committed: Thu Jul 2 19:51:40 2015 +0800
----------------------------------------------------------------------
conf/kylin.properties | 12 +
.../test_case_data/sandbox/kylin.properties | 14 +
monitor/pom.xml | 169 +
.../apache/kylin/monitor/ApiRequestParser.java | 241 +
.../java/org/apache/kylin/monitor/Client.java | 63 +
.../org/apache/kylin/monitor/ConfigUtils.java | 220 +
.../org/apache/kylin/monitor/DebugClient.java | 62 +
.../org/apache/kylin/monitor/FileUtils.java | 116 +
.../apache/kylin/monitor/HiveJdbcClient.java | 223 +
.../kylin/monitor/MonitorMetaManager.java | 217 +
.../org/apache/kylin/monitor/QueryParser.java | 249 +
monitor/src/main/resources/log4j.properties | 31 +
.../org/apache/kylin/monitor/ParseLogTest.java | 77 +
pom.xml | 1 +
script/prepare.sh | 2 +
server/pom.xml | 5 +
.../rest/controller/PerformanceController.java | 125 +
.../kylin/rest/filter/KylinApiFilter.java | 121 +
.../apache/kylin/rest/service/CubeService.java | 4 +
.../kylin/rest/service/PerformService.java | 123 +
server/src/main/resources/kylinSecurity.xml | 1 +
server/src/main/webapp/WEB-INF/web.xml | 122 +-
webapp/app/css/AdminLTE.css | 4782 ++++++++++--------
webapp/app/index.html | 16 +-
webapp/app/js/app.js | 2 +-
webapp/app/js/controllers/dashboard.js | 237 +
webapp/app/js/services/dashboard.js | 32 +
webapp/app/js/services/users.js | 13 +-
webapp/app/less/app.less | 17 +
webapp/app/partials/admin/admin.html | 3 +
webapp/app/partials/dashboard.html | 228 +
webapp/app/partials/header.html | 2 +-
webapp/app/routes.json | 7 +
webapp/bower.json | 10 +-
webapp/grunt.json | 2 +
35 files changed, 5391 insertions(+), 2158 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/conf/kylin.properties
----------------------------------------------------------------------
diff --git a/conf/kylin.properties b/conf/kylin.properties
index cf9756e..b1ebccc 100644
--- a/conf/kylin.properties
+++ b/conf/kylin.properties
@@ -101,3 +101,15 @@ deploy.env=DEV
###########################config info for sandbox#######################
kylin.sandbox=true
+
+
+###########################config info for kylin monitor#######################
+# hive jdbc url
+kylin.hive.jdbc.connection.url=
+
+#config where to parse query log,split with comma ,will also read $KYLIN_HOME/tomcat/logs/ by default
+ext.log.base.dir = /tmp/kylin_log1,/tmp/kylin_log2
+
+#will create external hive table to query result csv file
+#will set to kylin_query_log by default if not config here
+query.log.parse.result.table = kylin_query_log
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/examples/test_case_data/sandbox/kylin.properties
----------------------------------------------------------------------
diff --git a/examples/test_case_data/sandbox/kylin.properties b/examples/test_case_data/sandbox/kylin.properties
index efaef5c..8f73f64 100644
--- a/examples/test_case_data/sandbox/kylin.properties
+++ b/examples/test_case_data/sandbox/kylin.properties
@@ -102,3 +102,17 @@ deploy.env=DEV
###########################config info for sandbox#######################
kylin.sandbox=true
+
+###########################config info for kylin monitor#######################
+# hive jdbc url
+kylin.hive.jdbc.connection.url= jdbc:hive2://sandbox:10000
+
+#config where to parse query log,split with comma ,will also read $KYLIN_HOME/tomcat/logs/ by default
+ext.log.base.dir = /tmp/kylin_log1,/tmp/kylin_log2
+
+#will create external hive table to query result csv file
+#will set to kylin_query_log by default if not config here
+query.log.parse.result.table = kylin_query_log
+
+
+
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/monitor/pom.xml
----------------------------------------------------------------------
diff --git a/monitor/pom.xml b/monitor/pom.xml
new file mode 100644
index 0000000..a6fc87c
--- /dev/null
+++ b/monitor/pom.xml
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.kylin</groupId>
+ <artifactId>kylin</artifactId>
+ <version>0.7.2-incubating-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>kylin-monitor</artifactId>
+ <packaging>jar</packaging>
+ <name>Kylin:Monitor</name>
+ <url>http://maven.apache.org</url>
+
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <hadoop.version>2.6.0</hadoop.version>
+ <hive.version>0.14.0</hive.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.17</version>
+ </dependency>
+ <dependency>
+ <groupId>net.sf.opencsv</groupId>
+ <artifactId>opencsv</artifactId>
+ <version>2.3</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-hdfs</artifactId>
+ <version>${hadoop.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-auth</artifactId>
+ <version>${hadoop.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-common</artifactId>
+ <version>${hadoop.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <!--hbase dependency-->
+ <dependency>
+ <groupId>org.apache.hbase</groupId>
+ <artifactId>hbase-hadoop2-compat</artifactId>
+ <version>${hbase-hadoop2.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hbase</groupId>
+ <artifactId>hbase-common</artifactId>
+ <version>${hbase-hadoop2.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hbase</groupId>
+ <artifactId>hbase-client</artifactId>
+ <version>${hbase-hadoop2.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hbase</groupId>
+ <artifactId>hbase-server</artifactId>
+ <version>${hbase-hadoop2.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!--hbase dependency-->
+ <dependency>
+ <groupId>org.apache.hbase</groupId>
+ <artifactId>hbase-hadoop2-compat</artifactId>
+ <version>${hbase-hadoop2.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hbase</groupId>
+ <artifactId>hbase-common</artifactId>
+ <version>${hbase-hadoop2.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hbase</groupId>
+ <artifactId>hbase-client</artifactId>
+ <version>${hbase-hadoop2.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hbase</groupId>
+ <artifactId>hbase-server</artifactId>
+ <version>${hbase-hadoop2.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <!-- Hive dependencies -->
+ <dependency>
+ <groupId>org.apache.hive</groupId>
+ <artifactId>hive-jdbc</artifactId>
+ <version>${hive.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>org.apache.kylin.monitor.Client</mainClass>
+ </manifest>
+ </archive>
+
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/monitor/src/main/java/org/apache/kylin/monitor/ApiRequestParser.java
----------------------------------------------------------------------
diff --git a/monitor/src/main/java/org/apache/kylin/monitor/ApiRequestParser.java b/monitor/src/main/java/org/apache/kylin/monitor/ApiRequestParser.java
new file mode 100644
index 0000000..0321549
--- /dev/null
+++ b/monitor/src/main/java/org/apache/kylin/monitor/ApiRequestParser.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package org.apache.kylin.monitor;
+
+import au.com.bytecode.opencsv.CSVWriter;
+import org.apache.commons.io.filefilter.RegexFileFilter;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.*;
+import org.apache.log4j.Logger;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author jiazhong
+ */
+public class ApiRequestParser {
+
+ final static Logger logger = Logger.getLogger(ApiRequestParser.class);
+ final static Charset ENCODING = StandardCharsets.UTF_8;
+ static String REQUEST_PARSE_RESULT_PATH = null;
+ final static String REQUEST_LOG_FILE_PATTERN = "kylin_request.log.(\\d{4}-\\d{2}-\\d{2})$";
+ final static String REQUEST_LOG_PARSE_RESULT_FILENAME = "kylin_request_log.csv";
+ static String DEPLOY_ENV;
+
+
+ final static String[] KYLIN_REQUEST_CSV_HEADER = {"REQUESTER", "REQ_TIME","REQ_DATE", "URI", "METHOD", "QUERY_STRING", "PAYLOAD", "RESP_STATUS", "TARGET", "ACTION","DEPLOY_ENV"};
+
+ private ConfigUtils monitorConfig;
+
+ public ApiRequestParser() {
+ monitorConfig = ConfigUtils.getInstance();
+ try {
+ monitorConfig.loadMonitorParam();
+ DEPLOY_ENV = monitorConfig.getDeployEnv();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void start() throws IOException, ParseException {
+ ApiRequestParser.REQUEST_PARSE_RESULT_PATH = ConfigUtils.getInstance().getRequestLogParseResultDir() + REQUEST_LOG_PARSE_RESULT_FILENAME;
+ this.parseRequestInit();
+
+ //get api req log files have been read
+ String[] hasReadFiles = MonitorMetaManager.getReadApiReqLogFileList();
+
+ List<File> files = this.getRequestLogFiles();
+ for (File file : files) {
+ if (!Arrays.asList(hasReadFiles).contains(file.getName())) {
+ this.parseRequestLog(file.getPath(), ApiRequestParser.REQUEST_PARSE_RESULT_PATH);
+ MonitorMetaManager.markApiReqLogFileAsRead(file.getName());
+ }
+ }
+ }
+
+ public void parseRequestInit() throws IOException {
+ logger.info("parse api request initializing...");
+ FileSystem fs = null;
+ try {
+ Configuration conf = new Configuration();
+ fs = FileSystem.get(conf);
+ org.apache.hadoop.fs.Path path = new org.apache.hadoop.fs.Path(ApiRequestParser.REQUEST_PARSE_RESULT_PATH);
+ if (!fs.exists(path)) {
+ fs.create(path);
+
+ //need to close before get FileSystem again
+ fs.close();
+ this.writeResultToHdfs(ApiRequestParser.REQUEST_PARSE_RESULT_PATH, ApiRequestParser.KYLIN_REQUEST_CSV_HEADER);
+ }
+ } catch (Exception e) {
+ fs.close();
+ logger.info("Failed to init:", e);
+ }
+ }
+
+ //parse query log and convert to csv file to hdfs
+ public void parseRequestLog(String filePath, String dPath) throws ParseException, IOException {
+
+ logger.info("Start parsing kylin api request file " + filePath + " !");
+
+// writer config init
+ FileSystem fs = this.getHdfsFileSystem();
+ org.apache.hadoop.fs.Path resultStorePath = new org.apache.hadoop.fs.Path(dPath);
+ OutputStreamWriter writer = new OutputStreamWriter(fs.append(resultStorePath));
+ CSVWriter cwriter = new CSVWriter(writer,'|',CSVWriter.NO_QUOTE_CHARACTER);
+
+ Pattern p_available = Pattern.compile("/kylin/api/(cubes|user)+.*");
+ Pattern p_request = Pattern.compile("^.*\\[.*KylinApiFilter.logRequest.*\\].*REQUEST:.*REQUESTER=(.*);REQ_TIME=(\\w+ (\\d{4}-\\d{2}-\\d{2}).*);URI=(.*);METHOD=(.*);QUERY_STRING=(.*);PAYLOAD=(.*);RESP_STATUS=(.*);$");
+ Pattern p_uri = Pattern.compile("/kylin/api/(\\w+)(/.*/)*(.*)$");
+ Matcher m_available = p_available.matcher("");
+ Matcher m_request = p_request.matcher("");
+ Matcher m_uri = p_uri.matcher("");
+
+ Path path = Paths.get(filePath);
+ try {
+ BufferedReader reader = Files.newBufferedReader(path, ENCODING);
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ //reset the input
+ m_available.reset(line);
+ m_request.reset(line);
+
+ //filter unnecessary info
+ if (m_available.find()) {
+ //filter GET info
+ if (m_request.find() && !m_request.group(5).equals("GET")) {
+
+ List<String> groups = new ArrayList<String>();
+ for (int i = 1; i <= m_request.groupCount(); i++) {
+ groups.add(m_request.group(i));
+ }
+
+ String uri = m_request.group(4);
+ m_uri.reset(uri);
+ if (m_uri.find()) {
+
+ //add target
+ groups.add(m_uri.group(1));
+
+ //add action
+ if (m_uri.group(1).equals("cubes")) {
+ switch (m_request.group(5)) {
+ case "DELETE":
+ groups.add("drop");
+ break;
+ case "POST":
+ groups.add("save");
+ break;
+ default:
+ //add parse action
+ groups.add(m_uri.group(3));
+ break;
+ }
+ }
+
+ }
+ groups.add(DEPLOY_ENV);
+ String[] recordArray = groups.toArray(new String[groups.size()]);
+ //write to hdfs
+ cwriter.writeNext(recordArray);
+ }
+ }
+
+
+ }
+ } catch (IOException ex) {
+ logger.info("Failed to write to hdfs:", ex);
+ } finally {
+ writer.close();
+ cwriter.close();
+ fs.close();
+ }
+
+ logger.info("Finish parsing file " + filePath + " !");
+ }
+
+ public void writeResultToHdfs(String dPath, String[] record) throws IOException {
+ OutputStreamWriter writer = null;
+ CSVWriter cwriter = null;
+ FileSystem fs = null;
+ try {
+
+ fs = this.getHdfsFileSystem();
+ org.apache.hadoop.fs.Path resultStorePath = new org.apache.hadoop.fs.Path(dPath);
+ writer = new OutputStreamWriter(fs.append(resultStorePath));
+ cwriter = new CSVWriter(writer,'|',CSVWriter.NO_QUOTE_CHARACTER);
+ cwriter.writeNext(record);
+
+ } catch (IOException e) {
+ logger.info("Exception", e);
+ } finally {
+ writer.close();
+ cwriter.close();
+ fs.close();
+ }
+ }
+
+ public List<File> getRequestLogFiles() {
+ List<File> logFiles = new ArrayList<File>();
+
+// String request_log_file_pattern = monitorConfig.getRequestLogFilePattern();
+
+ List<String> request_log_dir_list = monitorConfig.getLogBaseDir();
+ FileFilter filter = new RegexFileFilter(REQUEST_LOG_FILE_PATTERN);
+
+ for (String path : request_log_dir_list) {
+ logger.info("fetch api request log file from path:" + path);
+ File request_log_dir = new File(path);
+ File[] request_log_files = request_log_dir.listFiles(filter);
+ if (request_log_files == null) {
+ logger.warn("no api request log file found under path" + path);
+ break;
+ }
+ Collections.addAll(logFiles, request_log_files);
+ }
+
+ return logFiles;
+ }
+
+ public FileSystem getHdfsFileSystem() throws IOException {
+ Configuration conf = new Configuration();
+// conf.set("dfs.client.block.write.replace-datanode-on-failure.policy", "NEVER");
+ FileSystem fs = null;
+ try {
+ fs = FileSystem.get(conf);
+ } catch (IOException e) {
+ fs.close();
+ logger.info("Failed to get hdfs FileSystem", e);
+ }
+ return fs;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/monitor/src/main/java/org/apache/kylin/monitor/Client.java
----------------------------------------------------------------------
diff --git a/monitor/src/main/java/org/apache/kylin/monitor/Client.java b/monitor/src/main/java/org/apache/kylin/monitor/Client.java
new file mode 100644
index 0000000..eadce54
--- /dev/null
+++ b/monitor/src/main/java/org/apache/kylin/monitor/Client.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+
+package org.apache.kylin.monitor;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+
+import java.io.File;
+
+
+/**
+ * Created by jiazhong on 2015/5/7.
+ */
+public class Client {
+
+
+ static {
+ //set monitor log path
+ String KYLIN_HOME = ConfigUtils.getKylinHome();
+ String CATALINA_HOME = null;
+ if(!StringUtils.isEmpty(KYLIN_HOME)){
+ CATALINA_HOME = ConfigUtils.getKylinHome() + File.separator + "tomcat"+File.separator;
+ System.out.println("will use "+CATALINA_HOME+"/logs to put monitor log");
+ }else{
+ CATALINA_HOME = "";
+ System.out.println("will use default path to put monitor log");
+ }
+ //log4j config will use this
+ System.setProperty("CATALINA_HOME",CATALINA_HOME);
+ }
+ final static Logger logger = Logger.getLogger(Client.class);
+
+
+
+ public static void main(String[] args) {
+ logger.info("monitor client start parsing...");
+ QueryParser queryParser = new QueryParser();
+ HiveJdbcClient jdbcClient = new HiveJdbcClient();
+ try {
+ queryParser.start();
+ jdbcClient.start();
+ } catch (Exception e) {
+ logger.info("Exception",e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/monitor/src/main/java/org/apache/kylin/monitor/ConfigUtils.java
----------------------------------------------------------------------
diff --git a/monitor/src/main/java/org/apache/kylin/monitor/ConfigUtils.java b/monitor/src/main/java/org/apache/kylin/monitor/ConfigUtils.java
new file mode 100644
index 0000000..f38b701
--- /dev/null
+++ b/monitor/src/main/java/org/apache/kylin/monitor/ConfigUtils.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+
+package org.apache.kylin.monitor;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import java.io.*;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Created by jiazhong on 2015/4/28.
+ */
+public class ConfigUtils {
+
+ final static Logger logger = Logger.getLogger(ConfigUtils.class);
+
+ private static ConfigUtils ourInstance = new ConfigUtils();
+
+ private Properties monitorConfig = new Properties();
+
+ public static ConfigUtils getInstance() {
+ return ourInstance;
+ }
+
+ private ConfigUtils() {
+ }
+
+ public static final String KYLIN_EXT_LOG_BASE_DIR = "ext.log.base.dir";
+ public static final String KYLIN_METADATA_URL = "kylin.metadata.url";
+
+ public static final String KYLIN_HOME = "KYLIN_HOME";
+ public static final String KYLIN_CONF = "KYLIN_CONF";
+ public static final String KYLIN_LOG_CONF_HOME = "KYLIN_LOG_CONF_HOME";
+ public static final String CATALINA_HOME = "CATALINA_HOME";
+
+ public static final String KYLIN_HDFS_WORKING_DIR = "kylin.hdfs.working.dir";
+
+ public static final String KYLIN_MONITOR_CONF_PROP_FILE = "kylin.properties";
+ public static final String QUERY_LOG_PARSE_RESULT_TABLE = "query.log.parse.result.table";
+ public static final String DEFAULT_QUERY_LOG_PARSE_RESULT_TABLE = "kylin_query_log";
+
+ public static final String DEPLOY_ENV = "deploy.env";
+
+
+ public static final String HIVE_JDBC_CON_URL = "kylin.hive.jdbc.connection.url";
+
+
+ public void loadMonitorParam() throws IOException {
+ Properties props = new Properties();
+ InputStream resourceStream = this.getKylinPropertiesAsInputSteam();
+ props.load(resourceStream);
+ this.monitorConfig = props;
+ }
+
+
+ public static InputStream getKylinPropertiesAsInputSteam() {
+ File propFile = getKylinMonitorProperties();
+ if (propFile == null || !propFile.exists()) {
+ logger.error("fail to locate kylin.properties");
+ throw new RuntimeException("fail to locate kylin.properties");
+ }
+ try {
+ return new FileInputStream(propFile);
+ } catch (FileNotFoundException e) {
+ logger.error("this should not happen");
+ throw new RuntimeException(e);
+ }
+
+ }
+
+
+ private static File getKylinMonitorProperties() {
+ String kylinConfHome = System.getProperty(KYLIN_CONF);
+ if (!StringUtils.isEmpty(kylinConfHome)) {
+ logger.info("load kylin.properties file from " + kylinConfHome + ". (from KYLIN_CONF System.property)");
+ return getKylinPropertiesFile(kylinConfHome);
+ }
+
+ logger.warn("KYLIN_CONF property was not set, will seek KYLIN_HOME env variable");
+
+ String kylinHome = getKylinHome();
+ if (StringUtils.isEmpty(kylinHome))
+ throw new RuntimeException("Didn't find KYLIN_CONF or KYLIN_HOME, please set one of them");
+
+ String path = kylinHome + File.separator + "conf";
+ logger.info("load kylin.properties file from " + kylinHome+". (from KYLIN_HOME System.env)");
+ return getKylinPropertiesFile(path);
+
+ }
+
+ public static String getKylinHome() {
+ String kylinHome = System.getenv(KYLIN_HOME);
+ if (StringUtils.isEmpty(kylinHome)) {
+ logger.warn("KYLIN_HOME was not set");
+ return kylinHome;
+ }
+ logger.info("KYLIN_HOME is :"+kylinHome);
+ return kylinHome;
+ }
+
+ private static File getKylinPropertiesFile(String path) {
+ if (path == null) {
+ return null;
+ }
+ return new File(path, KYLIN_MONITOR_CONF_PROP_FILE);
+ }
+
+
+ /*
+ * get where to path log
+ */
+ public List<String> getLogBaseDir() {
+ List<String> logDirList = new ArrayList<String>();
+
+ String kylinLogConfHome = System.getProperty(KYLIN_LOG_CONF_HOME);
+ if (!StringUtils.isEmpty(kylinLogConfHome)) {
+ logger.info("Use KYLIN_LOG_CONF_HOME=" + KYLIN_LOG_CONF_HOME);
+ logDirList.add(kylinLogConfHome);
+ }
+
+ String kylinExtLogBaseDir = getExtLogBaseDir();
+ if (!StringUtils.isEmpty(kylinExtLogBaseDir)) {
+ String[] extPaths = kylinExtLogBaseDir.split(",");
+ for(String path:extPaths){
+ if(!StringUtils.isEmpty(path)){
+ logger.info("Use ext log dir=" + path);
+ logDirList.add(path.trim());
+ }
+ }
+ }
+
+ String kylinHome = getKylinHome();
+ if (!StringUtils.isEmpty(kylinHome))
+ if(logDirList.isEmpty()){
+ throw new RuntimeException("Didn't find KYLIN_CONF or KYLIN_HOME or KYLIN_EXT_LOG_BASE_DIR, please set one of them");
+ }
+ else{
+ String path = kylinHome + File.separator + "tomcat" + File.separator + "logs";
+ logDirList.add(path);
+ }
+
+ return logDirList;
+ }
+
+
+ public String getMetadataUrl(){
+ return this.monitorConfig.getProperty(KYLIN_METADATA_URL);
+ }
+
+ public String getExtLogBaseDir() {
+ return this.monitorConfig.getProperty(KYLIN_EXT_LOG_BASE_DIR);
+ }
+
+ public String getKylinHdfsWorkingDir() {
+ return this.monitorConfig.getProperty(KYLIN_HDFS_WORKING_DIR);
+ }
+ public String getQueryLogParseResultDir() {
+ return this.getKylinHdfsWorkingDir()+"/performance/query/";
+ }
+
+ public String getQueryLogResultTable() {
+ String query_log_parse_result_table = this.monitorConfig.getProperty(QUERY_LOG_PARSE_RESULT_TABLE);
+ if(!StringUtils.isEmpty(query_log_parse_result_table)){
+ return query_log_parse_result_table;
+ }else{
+ return DEFAULT_QUERY_LOG_PARSE_RESULT_TABLE;
+ }
+ }
+
+ public String getRequestLogParseResultDir() {
+ return this.getKylinHdfsWorkingDir()+"/performance/request/";
+ }
+
+ public String getHiveJdbcConUrl() {
+ return this.monitorConfig.getProperty(HIVE_JDBC_CON_URL);
+ }
+
+ public String getLogParseResultMetaDir() {
+ return this.getKylinHdfsWorkingDir()+"/performance/metadata/";
+ }
+
+ public String getDeployEnv(){
+ return this.monitorConfig.getProperty(DEPLOY_ENV);
+ }
+
+
+ public static void addClasspath(String path) throws Exception {
+ File file = new File(path);
+
+ if (file.exists()) {
+ URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
+ Class<URLClassLoader> urlClass = URLClassLoader.class;
+ Method method = urlClass.getDeclaredMethod("addURL", new Class[] { URL.class });
+ method.setAccessible(true);
+ method.invoke(urlClassLoader, new Object[] { file.toURI().toURL() });
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/monitor/src/main/java/org/apache/kylin/monitor/DebugClient.java
----------------------------------------------------------------------
diff --git a/monitor/src/main/java/org/apache/kylin/monitor/DebugClient.java b/monitor/src/main/java/org/apache/kylin/monitor/DebugClient.java
new file mode 100644
index 0000000..a7230e6
--- /dev/null
+++ b/monitor/src/main/java/org/apache/kylin/monitor/DebugClient.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+
+package org.apache.kylin.monitor;
+
+import org.apache.log4j.Logger;
+import java.io.File;
+
+
+/**
+ * Created by jiazhong on 2015/5/7
+ */
+public class DebugClient {
+
+ static{
+ //set catalina.home temp
+ System.setProperty(ConfigUtils.CATALINA_HOME, "../server/");
+ }
+
+ final static Logger logger = Logger.getLogger(DebugClient.class);
+
+
+ public static void main(String[] args) throws Exception {
+
+ // test_case_data/sandbox/ contains HDP 2.2 site xmls which is dev sandbox
+ ConfigUtils.addClasspath(new File("../examples/test_case_data/sandbox").getAbsolutePath());
+
+ //set log base dir ,will also get from $KYLIN_HOME/tomcat/logs and config [ext.log.base.dir] in kylin.properties
+ System.setProperty(ConfigUtils.KYLIN_LOG_CONF_HOME, "../server/logs");
+
+ //get kylin.properties ,if not exist will get from $KYLIN_HOME/conf/
+ System.setProperty(ConfigUtils.KYLIN_CONF, "../examples/test_case_data/sandbox");
+
+
+ QueryParser queryParser = new QueryParser();
+ HiveJdbcClient jdbcClient = new HiveJdbcClient();
+
+ try {
+ queryParser.start();
+ jdbcClient.start();
+ } catch (Exception e) {
+ logger.info("Exception ",e);
+ }
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/monitor/src/main/java/org/apache/kylin/monitor/FileUtils.java
----------------------------------------------------------------------
diff --git a/monitor/src/main/java/org/apache/kylin/monitor/FileUtils.java b/monitor/src/main/java/org/apache/kylin/monitor/FileUtils.java
new file mode 100644
index 0000000..555409b
--- /dev/null
+++ b/monitor/src/main/java/org/apache/kylin/monitor/FileUtils.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+
+package org.apache.kylin.monitor;
+
+import au.com.bytecode.opencsv.CSVWriter;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.log4j.Logger;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+/**
+ * Created by jiazhong on 2015/6/18.
+ */
+public class FileUtils {
+
+ final static Logger logger = Logger.getLogger(FileUtils.class);
+
+ public static boolean pathCheck(String filePath) throws IOException {
+ logger.info("checking file:"+filePath);
+ FileSystem fs = null;
+ try {
+ Configuration conf = new Configuration();
+ fs = FileSystem.get(conf);
+ org.apache.hadoop.fs.Path path = new org.apache.hadoop.fs.Path(filePath);
+ if (!fs.exists(path)) {
+ fs.create(path);
+ fs.close();
+ return false;
+ }
+ } catch (Exception e) {
+ fs.close();
+ logger.info("Failed to init:", e);
+ }
+ return true;
+ }
+
+
+ /*
+ * write parse result to hdfs
+ */
+ public static void clearHdfsFile(String dPath) throws IOException {
+ OutputStreamWriter writer = null;
+ FileSystem fs = null;
+ try {
+ fs = getHdfsFileSystem();
+ org.apache.hadoop.fs.Path resultStorePath = new org.apache.hadoop.fs.Path(dPath);
+ writer = new OutputStreamWriter(fs.create(resultStorePath, true));
+
+ } catch (Exception e) {
+ logger.info("Exception", e);
+ } finally {
+ writer.close();
+ fs.close();
+ }
+ }
+
+ /*
+ * write parse result to hdfs
+ */
+ public static void appendResultToHdfs(String dPath, String[] record) throws IOException {
+ OutputStreamWriter writer = null;
+ CSVWriter cwriter = null;
+ FileSystem fs = null;
+ try {
+ fs = getHdfsFileSystem();
+ org.apache.hadoop.fs.Path resultStorePath = new org.apache.hadoop.fs.Path(dPath);
+ writer = new OutputStreamWriter(fs.append(resultStorePath));
+ cwriter = new CSVWriter(writer, '|', CSVWriter.NO_QUOTE_CHARACTER);
+
+ cwriter.writeNext(record);
+
+ } catch (Exception e) {
+ logger.info("Exception", e);
+ } finally {
+ writer.close();
+ cwriter.close();
+ fs.close();
+ }
+ }
+
+ /*
+ * get hdfs fileSystem
+ */
+ public static FileSystem getHdfsFileSystem() throws IOException {
+ Configuration conf = new Configuration();
+// conf.set("dfs.client.block.write.replace-datanode-on-failure.policy", "NEVER");
+ FileSystem fs = null;
+ try {
+ fs = FileSystem.newInstance(conf);
+ } catch (IOException e) {
+ fs.close();
+ logger.info("Failed to get hdfs FileSystem", e);
+ }
+ return fs;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/monitor/src/main/java/org/apache/kylin/monitor/HiveJdbcClient.java
----------------------------------------------------------------------
diff --git a/monitor/src/main/java/org/apache/kylin/monitor/HiveJdbcClient.java b/monitor/src/main/java/org/apache/kylin/monitor/HiveJdbcClient.java
new file mode 100644
index 0000000..71a3a68
--- /dev/null
+++ b/monitor/src/main/java/org/apache/kylin/monitor/HiveJdbcClient.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+
+package org.apache.kylin.monitor;
+
+import org.apache.log4j.Logger;
+import org.datanucleus.util.StringUtils;
+
+import java.io.IOException;
+import java.sql.*;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+/**
+ * Created by jiazhong on 2015/6/17.
+ */
+public class HiveJdbcClient {
+
+ static String SQL_GENERATE_QUERY_LOG_TABLE = "CREATE EXTERNAL TABLE IF NOT EXISTS [QUERY_LOG_TABLE_NAME] (REQUEST_TIME STRING,REQUEST_DATE DATE, QUERY_SQL STRING,QUERY_USER STRING,IS_SUCCESS STRING,QUERY_LATENCY DECIMAL(19,4),QUERY_PROJECT STRING,REALIZATION_NAMES STRING,CUBOID_IDS STRING,TOTAL_SCAN_COUNT INT,RESULT_ROW_COUNT INT,ACCEPT_PARTIAL STRING,IS_PARTIAL_RESULT STRING,HIT_CACHE STRING,MESSAGE STRING,DEPLOY_ENV STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' LINES TERMINATED BY '\\n' LOCATION '[QUERY_LOG_PARSE_RESULT_DIR]' TBLPROPERTIES (\"SKIP.HEADER.LINE.COUNT\"=\"1\")";
+
+ static String SQL_TOTAL_QUERY_USER = "SELECT COUNT(DISTINCT QUERY_USER) FROM [QUERY_LOG_TABLE_NAME]";
+
+ static String SQL_AVG_DAY_QUERY = "SELECT AVG(A.COUNT_QUERY) FROM (SELECT COUNT(*) COUNT_QUERY,REQUEST_DATE FROM [QUERY_LOG_TABLE_NAME] GROUP BY REQUEST_DATE) A";
+
+ static String SQL_LAST_30_DAYILY_QUERY_COUNT = "SELECT REQUEST_DATE, COUNT(*) FROM [QUERY_LOG_TABLE_NAME] WHERE REQUEST_DATE>=[START_DATE] AND REQUEST_DATE<[END_DATE] GROUP BY REQUEST_DATE";
+
+
+ //last 30 days
+ static String SQL_90_PERCENTTILE_LAST_30_DAY = "SELECT PERCENTILE_APPROX(LOG.QUERY_LATENCY,0.9) FROM (SELECT QUERY_LATENCY FROM [QUERY_LOG_TABLE_NAME] WHERE IS_SUCCESS='true' AND REQUEST_DATE>=[START_DATE] AND REQUEST_DATE<[END_DATE]) LOG";
+
+ //0.9,0.95 [each day] percentile in last 30 days
+ static String SQL_EACH_DAY_PERCENTILE = "SELECT REQUEST_DATE, PERCENTILE_APPROX(QUERY_LATENCY,ARRAY(0.9,0.95)),COUNT(*) FROM [QUERY_LOG_TABLE_NAME] WHERE IS_SUCCESS='true' AND REQUEST_DATE>=[START_DATE] AND REQUEST_DATE<[END_DATE] GROUP BY REQUEST_DATE";
+
+ //0.9,0.95 [project] percentile in last 30 days
+ static String SQL_DAY_PERCENTILE_BY_PROJECT = "SELECT QUERY_PROJECT, PERCENTILE_APPROX(QUERY_LATENCY,ARRAY(0.9,0.95)) FROM [QUERY_LOG_TABLE_NAME] WHERE IS_SUCCESS='true' AND REQUEST_DATE>=[START_DATE] AND REQUEST_DATE<[END_DATE] GROUP BY QUERY_PROJECT";
+
+
+
+ static String QUERY_LOG_TABLE_NAME = "KYLIN_QUERY_LOG";
+
+ final static Logger logger = Logger.getLogger(HiveJdbcClient.class);
+
+
+ private static String driverName = "org.apache.hive.jdbc.HiveDriver";
+ private static ConfigUtils monitorConfig = ConfigUtils.getInstance();
+
+ static {
+ try {
+ Class.forName(driverName);
+ monitorConfig.loadMonitorParam();
+ QUERY_LOG_TABLE_NAME = monitorConfig.getQueryLogResultTable();
+ if (StringUtils.isEmpty(QUERY_LOG_TABLE_NAME)) {
+ logger.error("table name not defined ,please set param [query.log.parse.result.table] in kylin.properties");
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ /*
+ * will create external hive table for [query] log parse result csv file on hdfs
+ * and will generate metric data in csv file on hdfs for web dashboard
+ */
+ public void start() throws SQLException, IOException {
+
+ String CON_URL = monitorConfig.getHiveJdbcConUrl();
+
+ Connection con = DriverManager.getConnection(CON_URL, "", "");
+ Statement stmt = con.createStatement();
+ ResultSet res = null;
+
+ SQL_GENERATE_QUERY_LOG_TABLE = generateQueryLogSql();
+ logger.info("Running Sql (Create Table):" + SQL_GENERATE_QUERY_LOG_TABLE);
+ stmt.execute(SQL_GENERATE_QUERY_LOG_TABLE);
+
+ SQL_TOTAL_QUERY_USER = generateUserCountSql();
+ logger.info("Running Sql (Total User):" + SQL_TOTAL_QUERY_USER);
+ res = stmt.executeQuery(SQL_TOTAL_QUERY_USER);
+
+ String total_query_user_path = monitorConfig.getLogParseResultMetaDir() + "total_query_user.csv";
+ FileUtils.pathCheck(total_query_user_path);
+ FileUtils.clearHdfsFile(total_query_user_path);
+ while (res.next()) {
+ FileUtils.appendResultToHdfs(total_query_user_path, new String[]{res.getString(1)});
+ logger.info("Total User:" + res.getString(1));
+ }
+
+ SQL_AVG_DAY_QUERY = generateAvgDayQuery();
+ logger.info("Running Sql (Avg Day Query):" + SQL_AVG_DAY_QUERY);
+ res = stmt.executeQuery(SQL_AVG_DAY_QUERY);
+
+ String avg_day_query_path = monitorConfig.getLogParseResultMetaDir() + "avg_day_query.csv";
+ FileUtils.pathCheck(avg_day_query_path);
+ FileUtils.clearHdfsFile(avg_day_query_path);
+ while (res.next()) {
+ FileUtils.appendResultToHdfs(avg_day_query_path, new String[]{res.getString(1)});
+ logger.info("avg day query:" + res.getString(1));
+ }
+
+
+ SQL_LAST_30_DAYILY_QUERY_COUNT = generateLast30DayilyQueryCount();
+ logger.info("Running Sql (Daily Query Count):" + SQL_LAST_30_DAYILY_QUERY_COUNT);
+ res = stmt.executeQuery(SQL_LAST_30_DAYILY_QUERY_COUNT);
+
+ String last_30_daily_query_count_path = monitorConfig.getLogParseResultMetaDir() + "last_30_daily_query_count.csv";
+ FileUtils.pathCheck(last_30_daily_query_count_path);
+ FileUtils.clearHdfsFile(last_30_daily_query_count_path);
+ while (res.next()) {
+ FileUtils.appendResultToHdfs(last_30_daily_query_count_path, new String[]{res.getString(1),res.getString(2)});
+ logger.info("last 30 daily query count:" + res.getString(1)+","+res.getString(2));
+ }
+
+
+ //90 percentile latency for all query in last 30 days
+ SQL_90_PERCENTTILE_LAST_30_DAY = generateNintyPercentileSql();
+ logger.info("Running Sql (last 30 days ,90 percentile query latency):" + SQL_90_PERCENTTILE_LAST_30_DAY);
+ res = stmt.executeQuery(SQL_90_PERCENTTILE_LAST_30_DAY);
+
+ String last_30_day_90_percentile_latency = monitorConfig.getLogParseResultMetaDir() + "last_30_day_90_percentile_latency.csv";
+ FileUtils.pathCheck(last_30_day_90_percentile_latency);
+ FileUtils.clearHdfsFile(last_30_day_90_percentile_latency);
+ while (res.next()) {
+ FileUtils.appendResultToHdfs(last_30_day_90_percentile_latency, new String[]{res.getString(1)});
+ logger.info("last 30 day 90 percentile latency:" + res.getString(1));
+ }
+
+ //90,95 project percentile latency for all query in last 30 days
+ SQL_DAY_PERCENTILE_BY_PROJECT = generateProjectPercentileSql();
+ logger.info("Running Sql (last 30 days ,90,95 percentile query latency by project):" + SQL_DAY_PERCENTILE_BY_PROJECT);
+ res = stmt.executeQuery(SQL_DAY_PERCENTILE_BY_PROJECT);
+
+ String last_30_day_project_percentile_latency_path = monitorConfig.getLogParseResultMetaDir() + "project_90_95_percentile_latency.csv";
+ FileUtils.pathCheck(last_30_day_project_percentile_latency_path);
+ FileUtils.clearHdfsFile(last_30_day_project_percentile_latency_path);
+ while (res.next() && res.getMetaData().getColumnCount() == 2) {
+ FileUtils.appendResultToHdfs(last_30_day_project_percentile_latency_path, new String[]{res.getString(1), res.getString(2)});
+ logger.info(res.getString(1) + "," + res.getString(2));
+ }
+
+ //0.9,0.95 percentile latency of every day in last 30 day
+ SQL_EACH_DAY_PERCENTILE = generateEachDayPercentileSql();
+ logger.info("Running sql (0.9,0.95 latency):" + SQL_EACH_DAY_PERCENTILE);
+ String each_day_percentile_file = monitorConfig.getLogParseResultMetaDir() + "each_day_90_95_percentile_latency.csv";
+ FileUtils.pathCheck(each_day_percentile_file);
+ FileUtils.clearHdfsFile(each_day_percentile_file);
+
+ res = stmt.executeQuery(SQL_EACH_DAY_PERCENTILE);
+ while (res.next() && res.getMetaData().getColumnCount() == 3) {
+ FileUtils.appendResultToHdfs(each_day_percentile_file, new String[]{res.getString(1), res.getString(2),res.getString(3)});
+ logger.info(res.getString(1) + "," + res.getString(2)+ "," + res.getString(3));
+ }
+
+ }
+
+ public String generateQueryLogSql() {
+ String query_log_parse_result_dir = monitorConfig.getQueryLogParseResultDir();
+ String query_log_table_name = monitorConfig.getQueryLogResultTable();
+ return SQL_GENERATE_QUERY_LOG_TABLE.replace("[QUERY_LOG_PARSE_RESULT_DIR]", query_log_parse_result_dir).replace("[QUERY_LOG_TABLE_NAME]", query_log_table_name);
+ }
+
+ public String generateUserCountSql() {
+ return SQL_TOTAL_QUERY_USER.replace("[QUERY_LOG_TABLE_NAME]", QUERY_LOG_TABLE_NAME);
+ }
+
+ public String generateAvgDayQuery() {
+ return SQL_AVG_DAY_QUERY.replace("[QUERY_LOG_TABLE_NAME]", QUERY_LOG_TABLE_NAME);
+ }
+
+ public String generateLast30DayilyQueryCount() {
+ SQL_LAST_30_DAYILY_QUERY_COUNT = SQL_LAST_30_DAYILY_QUERY_COUNT.replace("[QUERY_LOG_TABLE_NAME]", QUERY_LOG_TABLE_NAME);
+ return monthStasticSqlConvert(SQL_LAST_30_DAYILY_QUERY_COUNT);
+ }
+
+
+
+ //last 30 days
+ public String generateNintyPercentileSql() {
+ SQL_90_PERCENTTILE_LAST_30_DAY = SQL_90_PERCENTTILE_LAST_30_DAY.replace("[QUERY_LOG_TABLE_NAME]", QUERY_LOG_TABLE_NAME);
+ return monthStasticSqlConvert(SQL_90_PERCENTTILE_LAST_30_DAY);
+ }
+
+ //last 30 days,each day 90,95 percentile
+ public String generateProjectPercentileSql() {
+ SQL_DAY_PERCENTILE_BY_PROJECT = SQL_DAY_PERCENTILE_BY_PROJECT.replace("[QUERY_LOG_TABLE_NAME]", QUERY_LOG_TABLE_NAME);
+ return monthStasticSqlConvert(SQL_DAY_PERCENTILE_BY_PROJECT);
+ }
+
+ public String generateEachDayPercentileSql() {
+ SQL_EACH_DAY_PERCENTILE = SQL_EACH_DAY_PERCENTILE.replace("[QUERY_LOG_TABLE_NAME]", QUERY_LOG_TABLE_NAME);
+ return monthStasticSqlConvert(SQL_EACH_DAY_PERCENTILE);
+ }
+
+ public String monthStasticSqlConvert(String sql) {
+
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DATE, -1);
+ String endDate = format.format(cal.getTime());
+ cal.add(Calendar.DATE, -30);
+ String startDate = format.format(cal.getTime());
+ return sql.replace("[START_DATE]", "'" + startDate + "'").replace("[END_DATE]", "'" + endDate + "'");
+ }
+
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/monitor/src/main/java/org/apache/kylin/monitor/MonitorMetaManager.java
----------------------------------------------------------------------
diff --git a/monitor/src/main/java/org/apache/kylin/monitor/MonitorMetaManager.java b/monitor/src/main/java/org/apache/kylin/monitor/MonitorMetaManager.java
new file mode 100644
index 0000000..c01cf66
--- /dev/null
+++ b/monitor/src/main/java/org/apache/kylin/monitor/MonitorMetaManager.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+
+package org.apache.kylin.monitor;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.*;
+import org.apache.hadoop.hbase.client.*;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.log4j.Logger;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Created by jiazhong on 2015/5/25.
+ */
+public class MonitorMetaManager {
+
+ private static ConfigUtils monitorConfig = ConfigUtils.getInstance();
+
+ static String TABLE_NAME = "kylin_metadata";
+ final static String COLUMN_FAMILY = "f";
+ final static String COLUMN = "c";
+ final static String ROW_KEY_QUERY_READ_FILES = "/performance/query_log_files_already_read";
+ final static String ROW_KEY_QUERY_READING_FILE = "/performance/query_log_file_reading";
+ final static String ROW_KEY_QUERY_READING_FILE_LINE = "/performance/query_log_file_reading";
+
+
+ final static String ROW_KEY_API_REQ_LOG_READ_FILES = "/performance/api_req_log_files_already_read";
+
+
+ final static Logger logger = Logger.getLogger(MonitorMetaManager.class);
+
+ static Configuration conf = null;
+
+ static {
+ try {
+ monitorConfig.loadMonitorParam();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ conf = HBaseConfiguration.create();
+ }
+
+
+ /*
+ * meta data initialize
+ * @unused
+ */
+ public static void init() throws Exception {
+ MonitorMetaManager.TABLE_NAME = MonitorMetaManager.getMetadataUrlPrefix();
+ logger.info("Monitor Metadata Table :"+MonitorMetaManager.TABLE_NAME);
+ logger.info("init monitor metadata,create table if not exist");
+ MonitorMetaManager.creatTable(TABLE_NAME, new String[]{COLUMN_FAMILY});
+ }
+
+ public static String getMetadataUrlPrefix() {
+ String hbaseMetadataUrl = monitorConfig.getMetadataUrl();
+ String defaultPrefix = "kylin_metadata";
+ int cut = hbaseMetadataUrl.indexOf('@');
+ String tmp = cut < 0 ? defaultPrefix : hbaseMetadataUrl.substring(0, cut);
+ return tmp;
+ }
+
+
+ /*
+ * mark query file as read after parsing
+ */
+ public static void markQueryFileAsRead(String filename) throws IOException {
+ String read_query_log_file = MonitorMetaManager.getReadQueryLogFiles();
+ if(StringUtils.isEmpty(read_query_log_file)){
+ read_query_log_file = filename;
+ }else{
+ read_query_log_file = read_query_log_file.concat(",").concat(filename);
+ }
+ MonitorMetaManager.updateData(TABLE_NAME, ROW_KEY_QUERY_READ_FILES, COLUMN_FAMILY,COLUMN, read_query_log_file);
+ }
+
+ /*
+ * mark reading file for tracking
+ */
+ public static void markQueryReadingFile(String query_reading_file) throws IOException {
+ MonitorMetaManager.updateData(TABLE_NAME, ROW_KEY_QUERY_READING_FILE, COLUMN_FAMILY,COLUMN, query_reading_file);
+ }
+
+ /*
+ * mark reading line for tracking
+ */
+ public static void markQueryReadingLine(String line_num) throws IOException {
+ MonitorMetaManager.updateData(TABLE_NAME, ROW_KEY_QUERY_READING_FILE_LINE, COLUMN_FAMILY,COLUMN, line_num);
+ }
+
+
+ /*
+ * get has been read file name list
+ */
+ public static String[] getReadQueryLogFileList() throws IOException {
+ String fileList = MonitorMetaManager.getReadQueryLogFiles();
+ return fileList.split(",");
+ }
+
+ /*
+ * get has been read query log file
+ */
+ public static String getReadQueryLogFiles() throws IOException {
+ return getListWithRowkey(TABLE_NAME, ROW_KEY_QUERY_READ_FILES);
+ }
+
+
+ /*
+ * get has been read file
+ */
+ public static String getListWithRowkey(String table, String rowkey) throws IOException {
+ Result result = getResultByRowKey(table, rowkey);
+ String fileList = null;
+ if (result.list() != null) {
+ for (KeyValue kv : result.list()) {
+ fileList = Bytes.toString(kv.getValue());
+ }
+
+ }
+ fileList = fileList==null?"":fileList;
+ return fileList;
+ }
+
+
+ /*
+ * mark api req log file as read after parsing
+ */
+ public static void markApiReqLogFileAsRead(String filename) throws IOException {
+ String read_api_req_log_files = MonitorMetaManager.getReadApiReqLogFiles();
+ if (StringUtils.isEmpty(read_api_req_log_files)) {
+ read_api_req_log_files = filename;
+ } else {
+ read_api_req_log_files = read_api_req_log_files.concat(",").concat(filename);
+ }
+ MonitorMetaManager.updateData(TABLE_NAME, ROW_KEY_API_REQ_LOG_READ_FILES, COLUMN_FAMILY,COLUMN, read_api_req_log_files);
+ }
+
+
+ /*
+ * get has been read log file name list
+ */
+ public static String[] getReadApiReqLogFileList() throws IOException {
+ String fileList = MonitorMetaManager.getReadApiReqLogFiles();
+ return fileList.split(",");
+ }
+
+ /*
+ * get has been read api request log file
+ */
+ public static String getReadApiReqLogFiles() throws IOException {
+ return getListWithRowkey(TABLE_NAME, ROW_KEY_API_REQ_LOG_READ_FILES);
+ }
+
+
+ /*
+ * create table in hbase
+ */
+ public static void creatTable(String tableName, String[] family) throws Exception {
+ HBaseAdmin admin = new HBaseAdmin(conf);
+ HTableDescriptor desc = new HTableDescriptor(tableName);
+ for (int i = 0; i < family.length; i++) {
+ desc.addFamily(new HColumnDescriptor(family[i]));
+ }
+ if (admin.tableExists(tableName)) {
+ logger.info("table Exists!");
+ } else {
+ admin.createTable(desc);
+ logger.info("create table Success!");
+ }
+ }
+
+ /*
+ * update cell in hbase
+ */
+ public static void updateData(String tableName, String rowKey, String family,String column, String value) throws IOException {
+ HTable table = new HTable(conf, Bytes.toBytes(tableName));
+ Put put = new Put(rowKey.getBytes());
+ put.add(family.getBytes(), column.getBytes(), value.getBytes());
+ try {
+ table.put(put);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ logger.info("end insert data ......");
+ }
+
+ /*
+ * get result by rowkey
+ */
+ public static Result getResultByRowKey(String tableName, String rowKey) throws IOException {
+ HTable table = new HTable(conf, Bytes.toBytes(tableName));
+ Get get = new Get(Bytes.toBytes(rowKey));
+ Result result = table.get(get);
+ return result;
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/monitor/src/main/java/org/apache/kylin/monitor/QueryParser.java
----------------------------------------------------------------------
diff --git a/monitor/src/main/java/org/apache/kylin/monitor/QueryParser.java b/monitor/src/main/java/org/apache/kylin/monitor/QueryParser.java
new file mode 100644
index 0000000..fc8a403
--- /dev/null
+++ b/monitor/src/main/java/org/apache/kylin/monitor/QueryParser.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package org.apache.kylin.monitor;
+
+import au.com.bytecode.opencsv.CSVWriter;
+import org.apache.commons.io.filefilter.RegexFileFilter;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.*;
+import org.apache.log4j.Logger;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author jiazhong
+ */
+public class QueryParser {
+
+ final static Logger logger = Logger.getLogger(QueryParser.class);
+ final static Charset ENCODING = StandardCharsets.UTF_8;
+ final static String QUERY_LOG_FILE_PATTERN = "kylin_query.log.(\\d{4}-\\d{2}-\\d{2})$";
+ final static String QUERY_LOG_PARSE_RESULT_FILENAME = "kylin_query_log.csv";
+ static String QUERY_PARSE_RESULT_PATH = null;
+ static String DEPLOY_ENV;
+
+ final static String[] KYLIN_QUERY_CSV_HEADER = {"REQ_TIME","REQ_DATE", "SQL", "USER", "IS_SUCCESS", "LATENCY", "PROJECT", "REALIZATION NAMES", "CUBOID IDS", "TOTAL SCAN COUNT", "RESULT ROW COUNT", "ACCEPT PARTIAL", "IS PARTIAL RESULT", "HIT CACHE", "MESSAGE","DEPLOY_ENV"};
+
+ private ConfigUtils monitorConfig;
+
+ public QueryParser() {
+ monitorConfig = ConfigUtils.getInstance();
+ try {
+ monitorConfig.loadMonitorParam();
+ DEPLOY_ENV = monitorConfig.getDeployEnv();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /*
+ * will parse kylin query log files ,and append result to csv on hdfs
+ * files read will be marked,will not read again
+ */
+
+ public void start() throws IOException, ParseException {
+ QueryParser.QUERY_PARSE_RESULT_PATH = ConfigUtils.getInstance().getQueryLogParseResultDir() + QUERY_LOG_PARSE_RESULT_FILENAME;
+ this.parseQueryInit();
+
+ //get query file has been read
+ String[] hasReadFiles = MonitorMetaManager.getReadQueryLogFileList();
+
+ //get all log files
+ List<File> files = this.getQueryLogFiles();
+
+ for (File file : files) {
+ if (!Arrays.asList(hasReadFiles).contains(file.getName())) {
+ this.parseQueryLog(file.getPath(), QueryParser.QUERY_PARSE_RESULT_PATH);
+ MonitorMetaManager.markQueryFileAsRead(file.getName());
+ }
+ }
+ }
+
+ public void parseQueryInit() throws IOException {
+ logger.info("parse query initializing...");
+ FileSystem fs = null;
+ try {
+ Configuration conf = new Configuration();
+ fs = FileSystem.get(conf);
+ org.apache.hadoop.fs.Path path = new org.apache.hadoop.fs.Path(QueryParser.QUERY_PARSE_RESULT_PATH);
+ if (!fs.exists(path)) {
+ fs.create(path);
+ fs.close(); //need to close before get FileSystem again
+ this.writeResultToHdfs(QueryParser.QUERY_PARSE_RESULT_PATH, QueryParser.KYLIN_QUERY_CSV_HEADER);
+ }
+ } catch (IOException e) {
+ fs.close();
+ logger.info("Failed to init:", e);
+ }
+ }
+
+ //parse query log and convert to csv file to hdfs
+ public void parseQueryLog(String filePath, String dPath) throws ParseException, IOException {
+
+ logger.info("Start parsing file " + filePath + " !");
+
+// writer config init
+ FileSystem fs = this.getHdfsFileSystem();
+ org.apache.hadoop.fs.Path resultStorePath = new org.apache.hadoop.fs.Path(dPath);
+ OutputStreamWriter writer = new OutputStreamWriter(fs.append(resultStorePath));
+ CSVWriter cwriter = new CSVWriter(writer,'|',CSVWriter.NO_QUOTE_CHARACTER);
+
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
+ Pattern p_query_start = Pattern.compile("^\\[.*\\]:\\[(.*),.*\\]\\[.*\\]\\[.*QueryService.logQuery.*\\].*");
+ Pattern p_query_end = Pattern.compile("^Message:(.*)$");
+ Pattern p_query_body = Pattern.compile("^\\[.*\\]:\\[((\\d{4}-\\d{2}-\\d{2}).*)\\]\\[.*\\]\\[.*\\].*\n^=+\\[QUERY\\]=+\n^SQL:(.*)\n^User:(.*)\n^Success:(.*)\n^Duration:(.*)\n^Project:(.*)\n^(Realization Names|Cube Names): \\[(.*)\\]\n^Cuboid Ids: \\[(.*)\\]\n^Total scan count:(.*)\n^Result row count:(.*)\n^Accept Partial:(.*)\n(^Is Partial Result:(.*)\n)?^Hit Cache:(.*)\n^Message:(.*)", Pattern.MULTILINE);
+ Matcher m_query_start = p_query_start.matcher("");
+ Matcher m_query_end = p_query_end.matcher("");
+ Matcher m_query_body = p_query_body.matcher("");
+
+ boolean query_start = false;
+ StringBuffer query_body = new StringBuffer("");
+ Path path = Paths.get(filePath);
+ try {
+ BufferedReader reader = Files.newBufferedReader(path, ENCODING);
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ m_query_start.reset(line); //reset the input
+ m_query_end.reset(line);
+
+ // set start flag ,clear StringBuffer
+ if (m_query_start.find()) {
+ query_start = true;
+ query_body = new StringBuffer("");
+ }
+ if (query_start) {
+ query_body.append(line + "\n");
+ }
+ if (m_query_end.find()) {
+ query_start = false;
+ m_query_body.reset(query_body);
+ logger.info("parsing query...");
+ logger.info(query_body);
+// skip group(8) and group(14)
+ if (m_query_body.find()) {
+ ArrayList<String> groups = new ArrayList<String>();
+ int grp_count = m_query_body.groupCount();
+ for (int i = 1; i <= grp_count; i++) {
+ if (i != 8 && i != 14) {
+ String grp_item = m_query_body.group(i);
+ grp_item = grp_item ==null?"":grp_item.trim();
+ groups.add(grp_item);
+ }
+ }
+
+ long start_time = format.parse(groups.get(0)).getTime() - (int) (Double.parseDouble(groups.get(5)) * 1000);
+ groups.set(0, format.format(new Date(start_time)));
+ groups.add(DEPLOY_ENV);
+ String[] recordArray = groups.toArray(new String[groups.size()]);
+// write to hdfs
+ cwriter.writeNext(recordArray);
+
+ }
+
+ }
+
+ }
+ } catch (IOException ex) {
+ logger.info("Failed to write to hdfs:", ex);
+ } finally {
+ writer.close();
+ cwriter.close();
+ fs.close();
+ }
+
+ logger.info("Finish parsing file " + filePath + " !");
+
+ }
+
+ /*
+ * write parse result to hdfs
+ */
+ public void writeResultToHdfs(String dPath, String[] record) throws IOException {
+ OutputStreamWriter writer = null;
+ CSVWriter cwriter = null;
+ FileSystem fs = null;
+ try {
+ fs = this.getHdfsFileSystem();
+ org.apache.hadoop.fs.Path resultStorePath = new org.apache.hadoop.fs.Path(dPath);
+ writer = new OutputStreamWriter(fs.append(resultStorePath));
+ cwriter = new CSVWriter(writer,'|',CSVWriter.NO_QUOTE_CHARACTER);
+
+ cwriter.writeNext(record);
+
+ } catch (IOException e) {
+ logger.info("Exception", e);
+ } finally {
+ writer.close();
+ cwriter.close();
+ fs.close();
+ }
+ }
+
+
+ /*
+ * get all query log files
+ */
+ public List<File> getQueryLogFiles() {
+ List<File> logFiles = new ArrayList<File>();
+
+ List<String> query_log_dir_list = monitorConfig.getLogBaseDir();
+ FileFilter filter = new RegexFileFilter(QUERY_LOG_FILE_PATTERN);
+
+ for (String path : query_log_dir_list) {
+ logger.info("fetching query log file from path:" + path);
+ File query_log_dir = new File(path);
+ File[] query_log_files = query_log_dir.listFiles(filter);
+ if (query_log_files == null) {
+ logger.warn("no query log file found under path" + path);
+ continue;
+ }
+
+ Collections.addAll(logFiles, query_log_files);
+ }
+ return logFiles;
+ }
+
+
+ /*
+ * get hdfs fileSystem
+ */
+ public FileSystem getHdfsFileSystem() throws IOException {
+ Configuration conf = new Configuration();
+ conf.set("dfs.client.block.write.replace-datanode-on-failure.policy", "NEVER");
+ FileSystem fs = null;
+ try {
+ fs = FileSystem.get(conf);
+ } catch (IOException e) {
+ fs.close();
+ logger.info("Failed to get hdfs FileSystem", e);
+ }
+ return fs;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/monitor/src/main/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/monitor/src/main/resources/log4j.properties b/monitor/src/main/resources/log4j.properties
new file mode 100644
index 0000000..6dec581
--- /dev/null
+++ b/monitor/src/main/resources/log4j.properties
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+log4j.rootLogger=INFO,stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=L4J [%d{yyyy-MM-dd HH:mm:ss,SSS}][%p][%c] - %m%n
+
+log4j.appender.monitor=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.monitor.layout=org.apache.log4j.PatternLayout
+log4j.appender.monitor.File=${CATALINA_HOME}logs/kylin_monitor.log
+log4j.appender.monitor.layout.ConversionPattern=[%t]:[%d{yyyy-MM-dd HH:mm:ss,SSS}][%p][%l] - %m%n
+log4j.appender.monitor.Append=true
+
+#log4j.logger.org.apache.hadoop=ERROR
+log4j.logger.org.apache.kylin=DEBUG,monitor
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/monitor/src/test/java/org/apache/kylin/monitor/ParseLogTest.java
----------------------------------------------------------------------
diff --git a/monitor/src/test/java/org/apache/kylin/monitor/ParseLogTest.java b/monitor/src/test/java/org/apache/kylin/monitor/ParseLogTest.java
new file mode 100644
index 0000000..7b4e7b7
--- /dev/null
+++ b/monitor/src/test/java/org/apache/kylin/monitor/ParseLogTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+
+package org.apache.kylin.monitor;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+
+/**
+ * Created by jiazhong on 2015/5/27.
+ */
+public class ParseLogTest {
+
+ private QueryParser queryParser;
+ private ApiRequestParser apiReqParser;
+
+ @BeforeClass
+ public static void beforeClass() throws Exception {
+ //set catalina.home temp
+ System.setProperty(ConfigUtils.CATALINA_HOME, "../server/");
+
+ //test_case_data/sandbox/ contains HDP 2.2 site xmls which is dev sandbox
+ ConfigUtils.addClasspath(new File("../examples/test_case_data/sandbox").getAbsolutePath());
+
+ //get log base dir
+ System.setProperty(ConfigUtils.KYLIN_LOG_CONF_HOME, "../examples/test_case_data/performance_data");
+
+ //get kylin.properties
+ System.setProperty(ConfigUtils.KYLIN_CONF, "../examples/test_case_data/sandbox");
+
+ }
+
+
+ @Before
+ public void before(){
+ queryParser = new QueryParser();
+ apiReqParser = new ApiRequestParser();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testQueryParseing();
+ testApiReqParsing();
+ }
+
+ private void testQueryParseing() throws IOException, ParseException {
+ queryParser.getQueryLogFiles();
+ }
+
+ private void testApiReqParsing(){
+ apiReqParser.getRequestLogFiles();
+ }
+
+
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 5748328..cdd8191 100644
--- a/pom.xml
+++ b/pom.xml
@@ -557,6 +557,7 @@
<module>query</module>
<module>server</module>
<module>jdbc</module>
+ <module>monitor</module>
<module>invertedindex</module>
</modules>
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/script/prepare.sh
----------------------------------------------------------------------
diff --git a/script/prepare.sh b/script/prepare.sh
index a8b1c7d..f0702f8 100755
--- a/script/prepare.sh
+++ b/script/prepare.sh
@@ -33,11 +33,13 @@ cp server/target/kylin-server-${version}.war tomcat/webapps/kylin.war
cp job/target/kylin-job-${version}-job.jar lib/kylin-job-${version}.jar
cp storage/target/kylin-storage-${version}-coprocessor.jar lib/kylin-coprocessor-${version}.jar
cp jdbc/target/kylin-jdbc-${version}.jar lib/kylin-jdbc-${version}.jar
+cp monitor/target/kylin-monitor-${version}.jar lib/kylin-monitor-${version}.jar
# Copied file becomes 000 for some env (e.g. my Cygwin)
chmod 644 tomcat/webapps/kylin.war
chmod 644 lib/kylin-job-${version}.jar
chmod 644 lib/kylin-coprocessor-${version}.jar
chmod 644 lib/kylin-jdbc-${version}.jar
+chmod 644 lib/kylin-monitor-${version}.jar
echo "add js css to war"
if [ ! -d "webapp/dist" ]
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/server/pom.xml
----------------------------------------------------------------------
diff --git a/server/pom.xml b/server/pom.xml
index 1578c62..8500426 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -444,6 +444,11 @@
<version>${jetty.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.github.isrsal</groupId>
+ <artifactId>spring-mvc-logger</artifactId>
+ <version>0.2</version>
+ </dependency>
</dependencies>
<build>
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/server/src/main/java/org/apache/kylin/rest/controller/PerformanceController.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/kylin/rest/controller/PerformanceController.java b/server/src/main/java/org/apache/kylin/rest/controller/PerformanceController.java
new file mode 100644
index 0000000..735db51
--- /dev/null
+++ b/server/src/main/java/org/apache/kylin/rest/controller/PerformanceController.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package org.apache.kylin.rest.controller;
+
+import com.codahale.metrics.annotation.Metered;
+import com.codahale.metrics.annotation.Timed;
+import net.sf.ehcache.CacheManager;
+import org.apache.kylin.cube.CubeInstance;
+import org.apache.kylin.rest.service.CubeService;
+import org.apache.kylin.rest.service.PerformService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Handle query requests.
+ *
+ * @author xduo
+ */
+@Controller
+@RequestMapping(value = "/performance")
+public class PerformanceController extends BasicController {
+
+ private static final Logger logger = LoggerFactory.getLogger(PerformanceController.class);
+
+
+ @Autowired
+ private PerformService performService;
+
+ @Autowired
+ private CubeService cubeService;
+
+ @Autowired
+ private CacheManager cacheManager;
+
+ @RequestMapping(value = "/eachDayPercentile", method = RequestMethod.GET)
+ @ResponseBody
+ @Timed(name = "eachDayPercentile")
+ public List<String[]> eachDayPercentile() throws IOException {
+ return performService.eachDayPercentile();
+ }
+
+ @RequestMapping(value = "/projectPercentile", method = RequestMethod.GET)
+ @ResponseBody
+ @Timed(name = "projectPercentile")
+ public List<String[]> projectPercentile() throws IOException {
+ return performService.projectPercentile();
+ }
+
+
+ @RequestMapping(value = "/last30DayPercentile", method = RequestMethod.GET)
+ @ResponseBody
+ @Timed(name = "last30DayPercentile")
+ public List<String[]> last30DayPercentile() throws IOException {
+ return performService.last30DayPercentile();
+ }
+
+ @RequestMapping(value = "/cubesStorage", method = {RequestMethod.GET})
+ @ResponseBody
+ @Metered(name = "cubesStorage")
+ public List<CubeInstance> getCubeStorage() {
+ return cubeService.listAllCubes(null, null);
+ }
+
+
+ @RequestMapping(value = "/totalQueryUser", method = {RequestMethod.GET})
+ @ResponseBody
+ @Metered(name = "totalQueryUser")
+ public List<String[]> totalQueryUser() throws IOException {
+ return performService.getTotalQueryUser();
+ }
+
+ @RequestMapping(value = "/dailyQueryCount", method = {RequestMethod.GET})
+ @ResponseBody
+ @Metered(name = "dailyQueryCount")
+ public List<String[]> dailyQueryCount() throws IOException {
+ return performService.dailyQueryCount();
+ }
+
+ @RequestMapping(value = "/avgDayQuery", method = {RequestMethod.GET})
+ @ResponseBody
+ @Metered(name = "avgDayQuery")
+ public List<String[]> avgDayQuery() throws IOException {
+ return performService.avgDayQuery();
+ }
+
+ @RequestMapping(value = "/listCubes", method = {RequestMethod.GET})
+ @ResponseBody
+ @Metered(name = "listCubes")
+ public List<CubeInstance> getCubes() {
+ return cubeService.listAllCubes(null,null);
+ }
+
+
+
+ public void setCubeService(CubeService cubeService) {
+ this.cubeService = cubeService;
+ }
+
+ public void setPerformService(PerformService performService) {
+ this.performService = performService;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/server/src/main/java/org/apache/kylin/rest/filter/KylinApiFilter.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/kylin/rest/filter/KylinApiFilter.java b/server/src/main/java/org/apache/kylin/rest/filter/KylinApiFilter.java
new file mode 100644
index 0000000..035b57d
--- /dev/null
+++ b/server/src/main/java/org/apache/kylin/rest/filter/KylinApiFilter.java
@@ -0,0 +1,121 @@
+package org.apache.kylin.rest.filter;
+
+import com.github.isrsal.logging.RequestWrapper;
+import com.github.isrsal.logging.ResponseWrapper;
+import com.sun.jersey.core.util.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Created by jiazhong on 2015/4/20.
+ */
+
+public class KylinApiFilter extends OncePerRequestFilter {
+ protected static final Logger logger = LoggerFactory.getLogger(KylinApiFilter.class);
+ private static final String REQUEST_PREFIX = "Request: ";
+ private static final String RESPONSE_PREFIX = "Response: ";
+ private AtomicLong id = new AtomicLong(1L);
+
+ public KylinApiFilter() {
+ }
+
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ if(logger.isDebugEnabled()) {
+ long requestId = this.id.incrementAndGet();
+ request = new RequestWrapper(Long.valueOf(requestId), (HttpServletRequest)request);
+ response = new ResponseWrapper(Long.valueOf(requestId), (HttpServletResponse)response);
+ }
+
+ try {
+ filterChain.doFilter((ServletRequest)request, (ServletResponse)response);
+ } finally {
+ if(logger.isDebugEnabled()) {
+ this.logRequest((HttpServletRequest)request,(ResponseWrapper)response);
+// this.logResponse((ResponseWrapper)response);
+ }
+
+ }
+
+ }
+
+ private void logRequest(HttpServletRequest request,ResponseWrapper response) {
+ StringBuilder msg = new StringBuilder();
+ msg.append("REQUEST: ");
+ HttpSession session = request.getSession(true);
+ SecurityContext context = (SecurityContext)session.getAttribute("SPRING_SECURITY_CONTEXT");
+
+ String requester="";
+ if(context!=null){
+ Authentication authentication= context.getAuthentication();
+ if(authentication!=null){
+ requester = authentication.getName();
+ }
+
+ }else {
+ final String authorization = request.getHeader("Authorization");
+ if (authorization != null && authorization.startsWith("Basic")) {
+ // Authorization: Basic base64credentials
+ String base64Credentials = authorization.substring("Basic".length()).trim();
+ String credentials = new String(Base64.decode(base64Credentials), Charset.forName("UTF-8"));
+ // credentials = username:password
+ String[] values = credentials.split(":", 2);
+ requester = values[0];
+ }
+ }
+ msg.append("REQUESTER="+requester);
+
+ SimpleDateFormat format = new SimpleDateFormat("z yyyy-MM-dd HH:mm:ss");
+ msg.append(";REQ_TIME=" + format.format(new Date()));
+ msg.append(";URI=").append(request.getRequestURI());
+ msg.append(";METHOD=").append(request.getMethod());
+ msg.append(";QUERY_STRING=").append(request.getQueryString());
+ if(request instanceof RequestWrapper && !this.isMultipart(request)) {
+ RequestWrapper requestWrapper = (RequestWrapper)request;
+
+ try {
+ String e = requestWrapper.getCharacterEncoding() != null?requestWrapper.getCharacterEncoding():"UTF-8";
+ msg.append(";PAYLOAD=").append(new String(requestWrapper.toByteArray(), e));
+ } catch (UnsupportedEncodingException var6) {
+ logger.warn("Failed to parse request payload", var6);
+ }
+ }
+ msg.append(";RESP_STATUS="+response.getStatus()).append(";");
+
+ logger.debug(msg.toString());
+ }
+
+ private boolean isMultipart(HttpServletRequest request) {
+ return request.getContentType() != null && request.getContentType().startsWith("multipart/form-data");
+ }
+
+ private void logResponse(ResponseWrapper response) {
+ StringBuilder msg = new StringBuilder();
+ msg.append("RESPONSE: ");
+ msg.append("REQUEST_ID=").append(response.getId());
+
+ try {
+ msg.append("; payload=").append(new String(response.toByteArray(), response.getCharacterEncoding()));
+ } catch (UnsupportedEncodingException var4) {
+ logger.warn("Failed to parse response payload", var4);
+ }
+
+ logger.debug(msg.toString());
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/server/src/main/java/org/apache/kylin/rest/service/CubeService.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/kylin/rest/service/CubeService.java b/server/src/main/java/org/apache/kylin/rest/service/CubeService.java
index de97a7b..71e9f19 100644
--- a/server/src/main/java/org/apache/kylin/rest/service/CubeService.java
+++ b/server/src/main/java/org/apache/kylin/rest/service/CubeService.java
@@ -19,6 +19,9 @@
package org.apache.kylin.rest.service;
import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.kylin.common.KylinConfig;
import org.apache.kylin.common.util.HBaseRegionSizeCalculator;
@@ -556,4 +559,5 @@ public class CubeService extends BasicService {
}
+
}
[2/4] incubator-kylin git commit: KYLIN-792 ,add performance module
Posted by zh...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/webapp/app/css/AdminLTE.css
----------------------------------------------------------------------
diff --git a/webapp/app/css/AdminLTE.css b/webapp/app/css/AdminLTE.css
index ac467b8..e91795e 100644
--- a/webapp/app/css/AdminLTE.css
+++ b/webapp/app/css/AdminLTE.css
@@ -1,116 +1,143 @@
+@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic);
/*!
- * AdminLTE v1.2
- * Author: AlmsaeedStudio.com
+ * AdminLTE v2.1.0
+ * Author: Almsaeed Studio
+ * Website: Almsaeed Studio <http://almsaeedstudio.com>
* License: Open source - MIT
* Please visit http://opensource.org/licenses/MIT for more information
!*/
/*
- Core: General style
-----------------------------
-*/
+ * Core: General Layout Style
+ * -------------------------
+ */
html,
body {
- font-family: 'Source Sans Pro', sans-serif;
- -webkit-font-smoothing: antialiased;
min-height: 100%;
}
-a {
- color: #3c8dbc;
+.layout-boxed html,
+.layout-boxed body {
+ height: 100%;
}
-a:hover,
-a:active,
-a:focus {
- outline: none;
- text-decoration: none;
- color: #72afd2;
+body {
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ font-weight: 400;
+ overflow-x: hidden;
+ overflow-y: auto;
}
-/* Layouts */
+/* Layout */
.wrapper {
min-height: 100%;
+ position: relative;
+ overflow: hidden!important;
}
.wrapper:before,
.wrapper:after {
- display: table;
content: " ";
+ display: table;
}
.wrapper:after {
clear: both;
}
-/* Header */
-body > .header {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- z-index: 1030;
-}
-/* Define 2 column template */
-.right-side,
-.left-side {
+.layout-boxed .wrapper {
+ max-width: 1250px;
+ margin: 0 auto;
min-height: 100%;
- display: block;
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);
+ position: relative;
}
-/*right side - contins main content*/
-.right-side {
- background-color: #f9f9f9;
- margin-left: 220px;
+.layout-boxed {
+ background: url('../img/boxed-bg.jpg') repeat fixed;
}
-/*left side - contains sidebar*/
-.left-side {
- position: absolute;
- width: 220px;
- top: 0;
+/*
+ * Content Wrapper - contains the main content
+ * ```.right-side has been deprecated as of v2.0.0 in favor of .content-wrapper ```
+ */
+.content-wrapper,
+.right-side,
+.main-footer {
+ -webkit-transition: -webkit-transform 0.3s ease-in-out, margin 0.3s ease-in-out;
+ -moz-transition: -moz-transform 0.3s ease-in-out, margin 0.3s ease-in-out;
+ -o-transition: -o-transform 0.3s ease-in-out, margin 0.3s ease-in-out;
+ transition: transform 0.3s ease-in-out, margin 0.3s ease-in-out;
+ margin-left: 230px;
+ z-index: 820;
+}
+.layout-top-nav .content-wrapper,
+.layout-top-nav .right-side,
+.layout-top-nav .main-footer {
+ margin-left: 0;
}
-@media screen and (min-width: 992px) {
- .left-side {
- top: 50px;
- }
- /*Right side strech mode*/
- .right-side.strech {
+@media (max-width: 767px) {
+ .content-wrapper,
+ .right-side,
+ .main-footer {
margin-left: 0;
}
- .right-side.strech > .content-header {
- margin-top: 0px;
- }
- /* Left side collapse */
- .left-side.collapse-left {
- left: -220px;
- }
}
-/*Give content full width on xs screens*/
-@media screen and (max-width: 992px) {
- .right-side {
+@media (min-width: 768px) {
+ .sidebar-collapse .content-wrapper,
+ .sidebar-collapse .right-side,
+ .sidebar-collapse .main-footer {
margin-left: 0;
}
}
-/*
- By default the layout is not fixed but if you add the class .fixed to the body element
- the sidebar and the navbar will automatically become poisitioned fixed
-*/
-body.fixed > .header,
-body.fixed .left-side,
-body.fixed .navbar {
+@media (max-width: 767px) {
+ .sidebar-open .content-wrapper,
+ .sidebar-open .right-side,
+ .sidebar-open .main-footer {
+ -webkit-transform: translate(230px, 0);
+ -ms-transform: translate(230px, 0);
+ -o-transform: translate(230px, 0);
+ transform: translate(230px, 0);
+ }
+}
+.content-wrapper,
+.right-side {
+ min-height: 100%;
+ background-color: #ecf0f5;
+ z-index: 800;
+}
+.main-footer {
+ background: #fff;
+ padding: 15px;
+ color: #444;
+ border-top: 1px solid #d2d6de;
+}
+/* Fixed layout */
+.fixed .main-header,
+.fixed .main-sidebar,
+.fixed .left-side {
position: fixed;
}
-body.fixed > .header {
+.fixed .main-header {
top: 0;
right: 0;
left: 0;
}
-body.fixed .navbar {
- left: 0;
- right: 0;
+.fixed .content-wrapper,
+.fixed .right-side {
+ padding-top: 50px;
+}
+@media (max-width: 767px) {
+ .fixed .content-wrapper,
+ .fixed .right-side {
+ padding-top: 100px;
+ }
}
-body.fixed .wrapper {
- margin-top: 50px;
+.fixed.layout-boxed .wrapper {
+ max-width: 100%;
}
/* Content */
.content {
- padding: 20px 15px;
- background: #f9f9f9;
- overflow: auto;
+ min-height: 250px;
+ padding: 15px;
+ margin-right: auto;
+ margin-left: auto;
+ padding-left: 15px;
+ padding-right: 15px;
}
-/* Utility */
/* H1 - H6 font */
h1,
h2,
@@ -126,607 +153,747 @@ h6,
.h6 {
font-family: 'Source Sans Pro', sans-serif;
}
-/* All images should be responsive */
-img {
- max-width: 100% !important;
-}
-.sort-highlight {
- background: #f4f4f4;
- border: 1px dashed #ddd;
- margin-bottom: 10px;
-}
-/* 10px padding and margins */
-.pad {
- padding: 10px;
-}
-.margin {
- margin: 10px;
-}
-/* Display inline */
-.inline {
- display: inline;
- width: auto;
-}
-/* Background colors */
-.bg-red,
-.bg-yellow,
-.bg-aqua,
-.bg-blue,
-.bg-light-blue,
-.bg-green,
-.bg-navy,
-.bg-teal,
-.bg-olive,
-.bg-lime,
-.bg-orange,
-.bg-fuchsia,
-.bg-purple,
-.bg-maroon,
-.bg-black {
- color: #f9f9f9 !important;
-}
-.bg-gray {
- background-color: #eaeaec !important;
+/* General Links */
+a {
+ color: #3c8dbc;
}
-.bg-black {
- background-color: #222222 !important;
+a:hover,
+a:active,
+a:focus {
+ outline: none;
+ text-decoration: none;
+ color: #72afd2;
}
-.bg-red {
- background-color: #f56954 !important;
+/* Page Header */
+.page-header {
+ margin: 10px 0 20px 0;
+ font-size: 22px;
}
-.bg-yellow {
- background-color: #f39c12 !important;
+.page-header > small {
+ color: #666;
+ display: block;
+ margin-top: 5px;
}
-.bg-aqua {
- background-color: #00c0ef !important;
+/*
+ * Component: Main Header
+ * ----------------------
+ */
+.main-header {
+ position: relative;
+ max-height: 100px;
+ z-index: 1030;
}
-.bg-blue {
- background-color: #0073b7 !important;
+.main-header > .navbar {
+ -webkit-transition: margin-left 0.3s ease-in-out;
+ -o-transition: margin-left 0.3s ease-in-out;
+ transition: margin-left 0.3s ease-in-out;
+ margin-bottom: 0;
+ margin-left: 230px;
+ border: none;
+ min-height: 50px;
+ border-radius: 0;
}
-.bg-light-blue {
- background-color: #3c8dbc !important;
+.layout-top-nav .main-header > .navbar {
+ margin-left: 0!important;
}
-.bg-green {
- background-color: #00a65a !important;
+.main-header #navbar-search-input {
+ background: rgba(255, 255, 255, 0.2);
+ border-color: transparent;
}
-.bg-navy {
- background-color: #001f3f !important;
+.main-header #navbar-search-input:focus,
+.main-header #navbar-search-input:active {
+ border-color: rgba(0, 0, 0, 0.1) !important;
+ background: rgba(255, 255, 255, 0.9);
}
-.bg-teal {
- background-color: #39cccc !important;
+.main-header #navbar-search-input::-moz-placeholder {
+ color: #ccc;
+ opacity: 1;
}
-.bg-olive {
- background-color: #3d9970 !important;
+.main-header #navbar-search-input:-ms-input-placeholder {
+ color: #ccc;
}
-.bg-lime {
- background-color: #01ff70 !important;
+.main-header #navbar-search-input::-webkit-input-placeholder {
+ color: #ccc;
}
-.bg-orange {
- background-color: #ff851b !important;
+.main-header .navbar-custom-menu,
+.main-header .navbar-right {
+ float: right;
}
-.bg-fuchsia {
- background-color: #f012be !important;
+@media (max-width: 991px) {
+ .main-header .navbar-custom-menu a,
+ .main-header .navbar-right a {
+ color: inherit;
+ background: transparent;
+ }
}
-.bg-purple {
- background-color: #932ab6 !important;
+@media (max-width: 767px) {
+ .main-header .navbar-right {
+ float: none;
+ }
+ .navbar-collapse .main-header .navbar-right {
+ margin: 7.5px -15px;
+ }
+ .main-header .navbar-right > li {
+ color: inherit;
+ border: 0;
+ }
}
-.bg-maroon {
- background-color: #85144b !important;
+.main-header .sidebar-toggle {
+ float: left;
+ background-color: transparent;
+ background-image: none;
+ padding: 15px 15px;
+ font-family: fontAwesome;
}
-/* Text colors */
-.text-red {
- color: #f56954 !important;
+.main-header .sidebar-toggle:before {
+ content: "\f0c9";
}
-.text-yellow {
- color: #f39c12 !important;
+.main-header .sidebar-toggle:hover {
+ color: #fff;
}
-.text-aqua {
- color: #00c0ef !important;
+.main-header .sidebar-toggle:focus,
+.main-header .sidebar-toggle:active {
+ background: transparent;
}
-.text-blue {
- color: #0073b7 !important;
+.main-header .sidebar-toggle .icon-bar {
+ display: none;
}
-.text-black {
- color: #222222 !important;
+.main-header .navbar .nav > li.user > a > .fa,
+.main-header .navbar .nav > li.user > a > .glyphicon,
+.main-header .navbar .nav > li.user > a > .ion {
+ margin-right: 5px;
}
-.text-light-blue {
- color: #3c8dbc !important;
+.main-header .navbar .nav > li > a > .label {
+ position: absolute;
+ top: 9px;
+ right: 7px;
+ text-align: center;
+ font-size: 9px;
+ padding: 2px 3px;
+ line-height: .9;
}
-.text-green {
- color: #00a65a !important;
+.main-header .logo {
+ -webkit-transition: width 0.3s ease-in-out;
+ -o-transition: width 0.3s ease-in-out;
+ transition: width 0.3s ease-in-out;
+ display: block;
+ float: left;
+ height: 50px;
+ font-size: 20px;
+ line-height: 50px;
+ text-align: center;
+ width: 230px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ padding: 0 15px;
+ font-weight: 300;
+ overflow: hidden;
}
-.text-navy {
- color: #001f3f !important;
+.main-header .logo .logo-lg {
+ display: block;
}
-.text-teal {
- color: #39cccc !important;
+.main-header .logo .logo-mini {
+ display: none;
}
-.text-olive {
- color: #3d9970 !important;
+.main-header .navbar-brand {
+ color: #fff;
}
-.text-lime {
- color: #01ff70 !important;
+.content-header {
+ position: relative;
+ padding: 15px 15px 0 15px;
}
-.text-orange {
- color: #ff851b !important;
+.content-header > h1 {
+ margin: 0;
+ font-size: 24px;
}
-.text-fuchsia {
- color: #f012be !important;
+.content-header > h1 > small {
+ font-size: 15px;
+ display: inline-block;
+ padding-left: 4px;
+ font-weight: 300;
}
-.text-purple {
- color: #932ab6 !important;
+.content-header > .breadcrumb {
+ float: right;
+ background: transparent;
+ margin-top: 0px;
+ margin-bottom: 0;
+ font-size: 12px;
+ padding: 7px 5px;
+ position: absolute;
+ top: 15px;
+ right: 10px;
+ border-radius: 2px;
}
-.text-maroon {
- color: #85144b !important;
+.content-header > .breadcrumb > li > a {
+ color: #444;
+ text-decoration: none;
+ display: inline-block;
}
-/*Hide elements by display none only*/
-.hide {
- display: none !important;
+.content-header > .breadcrumb > li > a > .fa,
+.content-header > .breadcrumb > li > a > .glyphicon,
+.content-header > .breadcrumb > li > a > .ion {
+ margin-right: 5px;
}
-/* Remove borders */
-.no-border {
- border: 0px !important;
+.content-header > .breadcrumb > li + li:before {
+ content: '>\00a0';
}
-/* Remove padding */
-.no-padding {
- padding: 0px !important;
+@media (max-width: 991px) {
+ .content-header > .breadcrumb {
+ position: relative;
+ margin-top: 5px;
+ top: 0;
+ right: 0;
+ float: none;
+ background: #d2d6de;
+ padding-left: 10px;
+ }
+ .content-header > .breadcrumb li:before {
+ color: #97a0b3;
+ }
}
-/* Remove margins */
-.no-margin {
- margin: 0px !important;
+.navbar-toggle {
+ color: #fff;
+ border: 0;
+ margin: 0;
+ padding: 15px 15px;
}
-/* Remove box shadow */
-.no-shadow {
- box-shadow: none!important;
+@media (max-width: 991px) {
+ .navbar-custom-menu .navbar-nav > li {
+ float: left;
+ }
+ .navbar-custom-menu .navbar-nav {
+ margin: 0;
+ float: left;
+ }
+ .navbar-custom-menu .navbar-nav > li > a {
+ padding-top: 15px;
+ padding-bottom: 15px;
+ line-height: 20px;
+ }
}
-/* Don't display when printing */
-@media print {
- .no-print {
- display: none;
+@media (max-width: 767px) {
+ .main-header {
+ position: relative;
}
- .left-side,
- .header,
- .content-header {
- display: none;
+ .main-header .logo,
+ .main-header .navbar {
+ width: 100%;
+ float: none;
+ position: relative!important;
}
- .right-side {
+ .main-header .navbar {
margin: 0;
}
+ .main-header .navbar-custom-menu {
+ float: right;
+ }
+ .main-sidebar,
+ .left-side {
+ padding-top: 100px!important;
+ }
}
-/* Remove border radius */
-.flat {
- -webkit-border-radius: 0 !important;
- -moz-border-radius: 0 !important;
- border-radius: 0 !important;
-}
-/* Change the color of the striped tables */
-.table-striped > tbody > tr:nth-child(odd) > td,
-.table-striped > tbody > tr:nth-child(odd) > th {
- background-color: #f3f4f5;
-}
-.table.no-border,
-.table.no-border td,
-.table.no-border th {
- border: 0;
-}
-/* .text-center in tables */
-table.text-center,
-table.text-center td,
-table.text-center th {
- text-align: center;
-}
-.table.align th {
- text-align: left;
+@media (max-width: 991px) {
+ .navbar-collapse.pull-left {
+ float: none!important;
+ }
+ .navbar-collapse.pull-left + .navbar-custom-menu {
+ display: block;
+ position: absolute;
+ top: 0;
+ right: 40px;
+ }
}
-.table.align td {
- text-align: right;
+/*
+ * Component: Sidebar
+ * ------------------
+ */
+.main-sidebar,
+.left-side {
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding-top: 50px;
+ min-height: 100%;
+ width: 230px;
+ z-index: 810;
+ -webkit-transition: -webkit-transform 0.3s ease-in-out, width 0.3s ease-in-out;
+ -moz-transition: -moz-transform 0.3s ease-in-out, width 0.3s ease-in-out;
+ -o-transition: -o-transform 0.3s ease-in-out, width 0.3s ease-in-out;
+ transition: transform 0.3s ease-in-out, width 0.3s ease-in-out;
}
-.text-bold,
-.text-bold.table td,
-.text-bold.table th {
- font-weight: 700;
+@media (max-width: 767px) {
+ .main-sidebar,
+ .left-side {
+ -webkit-transform: translate(-230px, 0);
+ -ms-transform: translate(-230px, 0);
+ -o-transform: translate(-230px, 0);
+ transform: translate(-230px, 0);
+ }
}
-.border-radius-none {
- -webkit-border-radius: 0 !important;
- -moz-border-radius: 0 !important;
- border-radius: 0 !important;
+@media (min-width: 768px) {
+ .sidebar-collapse .main-sidebar,
+ .sidebar-collapse .left-side {
+ -webkit-transform: translate(-230px, 0);
+ -ms-transform: translate(-230px, 0);
+ -o-transform: translate(-230px, 0);
+ transform: translate(-230px, 0);
+ }
}
-/* _fix for sparkline tooltip */
-.jqstooltip {
- padding: 5px!important;
- width: auto!important;
- height: auto!important;
+@media (max-width: 767px) {
+ .sidebar-open .main-sidebar,
+ .sidebar-open .left-side {
+ -webkit-transform: translate(0, 0);
+ -ms-transform: translate(0, 0);
+ -o-transform: translate(0, 0);
+ transform: translate(0, 0);
+ }
}
-/*
-Gradient Background colors
-*/
-.bg-teal-gradient {
- background: #39cccc !important;
- background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #39cccc), color-stop(1, #7adddd)) !important;
- background: -ms-linear-gradient(bottom, #39cccc, #7adddd) !important;
- background: -moz-linear-gradient(center bottom, #39cccc 0%, #7adddd 100%) !important;
- background: -o-linear-gradient(#7adddd, #39cccc) !important;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#7adddd', endColorstr='#39cccc', GradientType=0) !important;
- color: #fff;
+.sidebar {
+ padding-bottom: 10px;
}
-.bg-light-blue-gradient {
- background: #3c8dbc !important;
- background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #3c8dbc), color-stop(1, #67a8ce)) !important;
- background: -ms-linear-gradient(bottom, #3c8dbc, #67a8ce) !important;
- background: -moz-linear-gradient(center bottom, #3c8dbc 0%, #67a8ce 100%) !important;
- background: -o-linear-gradient(#67a8ce, #3c8dbc) !important;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#67a8ce', endColorstr='#3c8dbc', GradientType=0) !important;
- color: #fff;
+.sidebar-form input:focus {
+ border-color: transparent!important;
}
-.bg-blue-gradient {
- background: #0073b7 !important;
- background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #0073b7), color-stop(1, #0089db)) !important;
- background: -ms-linear-gradient(bottom, #0073b7, #0089db) !important;
- background: -moz-linear-gradient(center bottom, #0073b7 0%, #0089db 100%) !important;
- background: -o-linear-gradient(#0089db, #0073b7) !important;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0089db', endColorstr='#0073b7', GradientType=0) !important;
- color: #fff;
+.user-panel {
+ position: relative;
+ width: 100%;
+ padding: 10px;
+ overflow: hidden;
}
-.bg-aqua-gradient {
- background: #00c0ef !important;
- background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #00c0ef), color-stop(1, #14d1ff)) !important;
- background: -ms-linear-gradient(bottom, #00c0ef, #14d1ff) !important;
- background: -moz-linear-gradient(center bottom, #00c0ef 0%, #14d1ff 100%) !important;
- background: -o-linear-gradient(#14d1ff, #00c0ef) !important;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#14d1ff', endColorstr='#00c0ef', GradientType=0) !important;
- color: #fff;
+.user-panel:before,
+.user-panel:after {
+ content: " ";
+ display: table;
}
-.bg-yellow-gradient {
- background: #f39c12 !important;
- background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #f39c12), color-stop(1, #f7bc60)) !important;
- background: -ms-linear-gradient(bottom, #f39c12, #f7bc60) !important;
- background: -moz-linear-gradient(center bottom, #f39c12 0%, #f7bc60 100%) !important;
- background: -o-linear-gradient(#f7bc60, #f39c12) !important;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f7bc60', endColorstr='#f39c12', GradientType=0) !important;
- color: #fff;
+.user-panel:after {
+ clear: both;
}
-.bg-purple-gradient {
- background: #932ab6 !important;
- background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #932ab6), color-stop(1, #b959d9)) !important;
- background: -ms-linear-gradient(bottom, #932ab6, #b959d9) !important;
- background: -moz-linear-gradient(center bottom, #932ab6 0%, #b959d9 100%) !important;
- background: -o-linear-gradient(#b959d9, #932ab6) !important;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b959d9', endColorstr='#932ab6', GradientType=0) !important;
- color: #fff;
+.user-panel > .image > img {
+ width: 100%;
+ max-width: 45px;
+ height: auto;
}
-.bg-green-gradient {
- background: #00a65a !important;
- background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #00a65a), color-stop(1, #00ca6d)) !important;
- background: -ms-linear-gradient(bottom, #00a65a, #00ca6d) !important;
- background: -moz-linear-gradient(center bottom, #00a65a 0%, #00ca6d 100%) !important;
- background: -o-linear-gradient(#00ca6d, #00a65a) !important;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ca6d', endColorstr='#00a65a', GradientType=0) !important;
- color: #fff;
+.user-panel > .info {
+ padding: 5px 5px 5px 15px;
+ line-height: 1;
+ position: absolute;
+ left: 55px;
}
-.bg-red-gradient {
- background: #f56954 !important;
- background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #f56954), color-stop(1, #f89384)) !important;
- background: -ms-linear-gradient(bottom, #f56954, #f89384) !important;
- background: -moz-linear-gradient(center bottom, #f56954 0%, #f89384 100%) !important;
- background: -o-linear-gradient(#f89384, #f56954) !important;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f89384', endColorstr='#f56954', GradientType=0) !important;
- color: #fff;
+.user-panel > .info > p {
+ font-weight: 600;
+ margin-bottom: 9px;
}
-.bg-black-gradient {
- background: #222222 !important;
- background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #222222), color-stop(1, #3c3c3c)) !important;
- background: -ms-linear-gradient(bottom, #222222, #3c3c3c) !important;
- background: -moz-linear-gradient(center bottom, #222222 0%, #3c3c3c 100%) !important;
- background: -o-linear-gradient(#3c3c3c, #222222) !important;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#3c3c3c', endColorstr='#222222', GradientType=0) !important;
- color: #fff;
+.user-panel > .info > a {
+ text-decoration: none;
+ padding-right: 5px;
+ margin-top: 3px;
+ font-size: 11px;
}
-.bg-maroon-gradient {
- background: #85144b !important;
- background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #85144b), color-stop(1, #b11b64)) !important;
- background: -ms-linear-gradient(bottom, #85144b, #b11b64) !important;
- background: -moz-linear-gradient(center bottom, #85144b 0%, #b11b64 100%) !important;
- background: -o-linear-gradient(#b11b64, #85144b) !important;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b11b64', endColorstr='#85144b', GradientType=0) !important;
- color: #fff;
+.user-panel > .info > a > .fa,
+.user-panel > .info > a > .ion,
+.user-panel > .info > a > .glyphicon {
+ margin-right: 3px;
}
-.connectedSortable {
- min-height: 100px;
+.sidebar-menu {
+ list-style: none;
+ margin: 0;
+ padding: 0;
}
-/*---------------------------------------------------
- LESS Elements 0.9
- ---------------------------------------------------
- A set of useful LESS mixins
- More info at: http://lesselements.com
- ---------------------------------------------------*/
-/*
- Components: navbar, logo and content header
--------------------------------------------------
-*/
-body > .header {
+.sidebar-menu > li {
position: relative;
- max-height: 100px;
- z-index: 1030;
-}
-body > .header .navbar {
- height: 50px;
- margin-bottom: 0;
- margin-left: 220px;
-}
-body > .header .navbar .sidebar-toggle {
- float: left;
- padding: 9px 5px;
- margin-top: 8px;
- margin-right: 0;
- margin-bottom: 8px;
- margin-left: 5px;
- background-color: transparent;
- background-image: none;
- border: 1px solid transparent;
- -webkit-border-radius: 0 !important;
- -moz-border-radius: 0 !important;
- border-radius: 0 !important;
-}
-body > .header .navbar .sidebar-toggle:hover .icon-bar {
- background: #f6f6f6;
+ margin: 0;
+ padding: 0;
}
-body > .header .navbar .sidebar-toggle .icon-bar {
+.sidebar-menu > li > a {
+ padding: 12px 5px 12px 15px;
display: block;
- width: 22px;
- height: 2px;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-body > .header .navbar .sidebar-toggle .icon-bar + .icon-bar {
- margin-top: 4px;
}
-body > .header .navbar .nav > li.user > a {
- font-weight: bold;
+.sidebar-menu > li > a > .fa,
+.sidebar-menu > li > a > .glyphicon,
+.sidebar-menu > li > a > .ion {
+ width: 20px;
}
-body > .header .navbar .nav > li.user > a > .fa,
-body > .header .navbar .nav > li.user > a > .glyphicon,
-body > .header .navbar .nav > li.user > a > .ion {
+.sidebar-menu > li .label,
+.sidebar-menu > li .badge {
+ margin-top: 3px;
margin-right: 5px;
}
-body > .header .navbar .nav > li > a > .label {
- -webkit-border-radius: 50%;
- -moz-border-radius: 50%;
- border-radius: 50%;
- position: absolute;
- top: 7px;
- right: 2px;
- font-size: 10px;
- font-weight: normal;
- width: 15px;
- height: 15px;
- line-height: 1.0em;
- text-align: center;
- padding: 2px;
-}
-body > .header .navbar .nav > li > a:hover > .label {
- top: 3px;
-}
-body > .header .logo {
- float: left;
- font-size: 20px;
- line-height: 50px;
- text-align: center;
- padding: 0 10px;
- width: 220px;
- font-family: 'Kaushan Script', cursive;
- font-weight: 500;
- height: 50px;
- display: block;
+.sidebar-menu li.header {
+ padding: 10px 25px 10px 15px;
+ font-size: 12px;
}
-body > .header .logo .icon {
+.sidebar-menu li > a > .fa-angle-left {
+ width: auto;
+ height: auto;
+ padding: 0;
margin-right: 10px;
+ margin-top: 3px;
}
-.right-side > .content-header {
- position: relative;
- padding: 15px 15px 10px 20px;
+.sidebar-menu li.active > a > .fa-angle-left {
+ -webkit-transform: rotate(-90deg);
+ -ms-transform: rotate(-90deg);
+ -o-transform: rotate(-90deg);
+ transform: rotate(-90deg);
+}
+.sidebar-menu li.active > .treeview-menu {
+ display: block;
}
-.right-side > .content-header > h1 {
+.sidebar-menu .treeview-menu {
+ display: none;
+ list-style: none;
+ padding: 0;
margin: 0;
- font-size: 24px;
+ padding-left: 5px;
}
-.right-side > .content-header > h1 > small {
- font-size: 15px;
- display: inline-block;
- padding-left: 4px;
- font-weight: 300;
+.sidebar-menu .treeview-menu .treeview-menu {
+ padding-left: 20px;
}
-.right-side > .content-header > .breadcrumb {
- float: right;
- background: transparent;
- margin-top: 0px;
- margin-bottom: 0;
- font-size: 12px;
- padding: 7px 5px;
- position: absolute;
- top: 15px;
- right: 10px;
- -webkit-border-radius: 2px;
- -moz-border-radius: 2px;
- border-radius: 2px;
+.sidebar-menu .treeview-menu > li {
+ margin: 0;
}
-.right-side > .content-header > .breadcrumb > li > a {
- color: #444;
- text-decoration: none;
+.sidebar-menu .treeview-menu > li > a {
+ padding: 5px 5px 5px 15px;
+ display: block;
+ font-size: 14px;
}
-.right-side > .content-header > .breadcrumb > li > a > .fa,
-.right-side > .content-header > .breadcrumb > li > a > .glyphicon,
-.right-side > .content-header > .breadcrumb > li > a > .ion {
- margin-right: 5px;
+.sidebar-menu .treeview-menu > li > a > .fa,
+.sidebar-menu .treeview-menu > li > a > .glyphicon,
+.sidebar-menu .treeview-menu > li > a > .ion {
+ width: 20px;
}
-.right-side > .content-header > .breadcrumb > li + li:before {
- content: '>\00a0';
+.sidebar-menu .treeview-menu > li > a > .fa-angle-left,
+.sidebar-menu .treeview-menu > li > a > .fa-angle-down {
+ width: auto;
}
-@media screen and (max-width: 767px) {
- .right-side > .content-header > .breadcrumb {
+/*
+ * Component: Sidebar Mini
+ */
+@media (min-width: 768px) {
+ .sidebar-mini.sidebar-collapse .content-wrapper,
+ .sidebar-mini.sidebar-collapse .right-side,
+ .sidebar-mini.sidebar-collapse .main-footer {
+ margin-left: 50px!important;
+ z-index: 840;
+ }
+ .sidebar-mini.sidebar-collapse .main-sidebar {
+ -webkit-transform: translate(0, 0);
+ -ms-transform: translate(0, 0);
+ -o-transform: translate(0, 0);
+ transform: translate(0, 0);
+ width: 50px!important;
+ z-index: 850;
+ }
+ .sidebar-mini.sidebar-collapse .sidebar-menu > li {
position: relative;
- margin-top: 5px;
+ }
+ .sidebar-mini.sidebar-collapse .sidebar-menu > li > a {
+ margin-right: 0;
+ }
+ .sidebar-mini.sidebar-collapse .sidebar-menu > li > a > span {
+ border-top-right-radius: 4px;
+ }
+ .sidebar-mini.sidebar-collapse .sidebar-menu > li:not(.treeview) > a > span {
+ border-bottom-right-radius: 4px;
+ }
+ .sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
+ padding-top: 5px;
+ padding-bottom: 5px;
+ border-bottom-right-radius: 4px;
+ }
+ .sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right),
+ .sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > .treeview-menu {
+ display: block!important;
+ position: absolute;
+ width: 180px;
+ left: 50px;
+ }
+ .sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span {
top: 0;
- right: 0;
- float: none;
- background: #efefef;
+ margin-left: -3px;
+ padding: 12px 5px 12px 20px;
+ background-color: inherit;
}
-}
-
-@media screen and (max-width: 560px) {
- body > .header {
- position: relative;
+ .sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > .treeview-menu {
+ top: 44px;
+ margin-left: 0;
}
- body > .header .logo,
- body > .header .navbar {
- width: 100%;
- float: none;
- position: relative!important;
+ .sidebar-mini.sidebar-collapse .main-sidebar .user-panel > .info,
+ .sidebar-mini.sidebar-collapse .sidebar-form,
+ .sidebar-mini.sidebar-collapse .sidebar-menu > li > a > span,
+ .sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu,
+ .sidebar-mini.sidebar-collapse .sidebar-menu > li > a > .pull-right,
+ .sidebar-mini.sidebar-collapse .sidebar-menu li.header {
+ display: none!important;
}
- body > .header .navbar {
- margin: 0;
+ .sidebar-mini.sidebar-collapse .main-header .logo {
+ width: 50px;
}
- body.fixed > .header {
- position: fixed;
+ .sidebar-mini.sidebar-collapse .main-header .logo > .logo-mini {
+ display: block;
+ margin-left: -15px;
+ margin-right: -15px;
+ font-size: 18px;
}
- body.fixed > .wrapper,
- body.fixed .sidebar-offcanvas {
- margin-top: 100px!important;
+ .sidebar-mini.sidebar-collapse .main-header .logo > .logo-lg {
+ display: none;
+ }
+ .sidebar-mini.sidebar-collapse .main-header .navbar {
+ margin-left: 50px;
}
}
-/*
- Component: Sidebar
---------------------------
-*/
-.sidebar {
- margin-bottom: 5px;
-}
-.sidebar .sidebar-form input:focus {
- -webkit-box-shadow: none;
- -moz-box-shadow: none;
- box-shadow: none;
- border-color: transparent!important;
+.sidebar-menu,
+.main-sidebar .user-panel,
+.sidebar-menu > li.header {
+ white-space: nowrap!important;
+ overflow: hidden;
}
-.sidebar .sidebar-menu {
- list-style: none;
- margin: 0;
- padding: 0;
+.sidebar-menu:hover {
+ overflow: visible;
}
-.sidebar .sidebar-menu > li {
- margin: 0;
- padding: 0;
+.sidebar-form,
+.sidebar-menu > li.header {
+ overflow: hidden;
+ text-overflow: clip;
}
-.sidebar .sidebar-menu > li > a {
- padding: 12px 5px 12px 15px;
- display: block;
+.sidebar-menu li > a {
+ position: relative;
}
-.sidebar .sidebar-menu > li > a > .fa,
-.sidebar .sidebar-menu > li > a > .glyphicon,
-.sidebar .sidebar-menu > li > a > .ion {
- width: 20px;
+.sidebar-menu li > a > .pull-right {
+ position: absolute;
+ top: 50%;
+ right: 10px;
+ margin-top: -7px;
}
-.sidebar .sidebar-menu .treeview-menu {
- display: none;
- list-style: none;
- padding: 0;
- margin: 0;
+/*
+ * Component: Control sidebar. By default, this is the right sidebar.
+ */
+.control-sidebar-bg {
+ position: fixed;
+ z-index: 1000;
+ bottom: 0;
}
-.sidebar .sidebar-menu .treeview-menu > li {
- margin: 0;
+.control-sidebar-bg,
+.control-sidebar {
+ top: 0;
+ right: -230px;
+ width: 230px;
+ -webkit-transition: right 0.3s ease-in-out;
+ -o-transition: right 0.3s ease-in-out;
+ transition: right 0.3s ease-in-out;
}
-.sidebar .sidebar-menu .treeview-menu > li > a {
- padding: 5px 5px 5px 15px;
+.control-sidebar {
+ position: absolute;
+ padding-top: 50px;
+ z-index: 1010;
+}
+@media (max-width: 768px) {
+ .control-sidebar {
+ padding-top: 100px;
+ }
+}
+.control-sidebar > .tab-content {
+ padding: 10px 15px;
+}
+.control-sidebar.control-sidebar-open,
+.control-sidebar.control-sidebar-open + .control-sidebar-bg {
+ right: 0;
+}
+.control-sidebar-open .control-sidebar-bg,
+.control-sidebar-open .control-sidebar {
+ right: 0;
+}
+@media (min-width: 768px) {
+ .control-sidebar-open .content-wrapper,
+ .control-sidebar-open .right-side,
+ .control-sidebar-open .main-footer {
+ margin-right: 230px;
+ }
+}
+.control-sidebar-tabs > li:first-of-type > a {
+ margin-left: 1px;
+}
+.control-sidebar-tabs > li:first-of-type > a,
+.control-sidebar-tabs > li:first-of-type > a:hover {
+ border-left-width: 0!important;
+}
+.control-sidebar-tabs > li > a {
+ border-radius: 0 !important;
+}
+.control-sidebar-tabs > li > a,
+.control-sidebar-tabs > li > a:hover {
+ border-top: none;
+ border-right: none;
+ border-left: 1px solid transparent!important;
+ border-bottom: 1px solid transparent!important;
+}
+.control-sidebar-tabs > li > a .icon {
+ font-size: 16px;
+}
+.control-sidebar-tabs > li.active > a,
+.control-sidebar-tabs > li.active > a:hover,
+.control-sidebar-tabs > li.active > a:focus,
+.control-sidebar-tabs > li.active > a:active {
+ border-top: none!important;
+ border-right: none!important;
+ border-bottom: none!important;
+}
+@media (max-width: 768px) {
+ .control-sidebar-tabs {
+ display: table;
+ }
+ .control-sidebar-tabs > li {
+ display: table-cell !important;
+ }
+}
+.control-sidebar-heading {
+ font-weight: 400;
+ font-size: 16px;
+ padding: 10px 0;
+ margin-bottom: 10px;
+}
+.control-sidebar-subheading {
display: block;
+ font-weight: 400;
font-size: 14px;
- margin: 0px 0px;
}
-.sidebar .sidebar-menu .treeview-menu > li > a > .fa,
-.sidebar .sidebar-menu .treeview-menu > li > a > .glyphicon,
-.sidebar .sidebar-menu .treeview-menu > li > a > .ion {
- width: 20px;
+.control-sidebar-menu {
+ list-style: none;
+ padding: 0;
+ margin: 0 -15px;
}
-.user-panel {
- padding: 10px;
+.control-sidebar-menu > li > a {
+ display: block;
+ padding: 10px 15px;
}
-.user-panel:before,
-.user-panel:after {
- display: table;
+.control-sidebar-menu > li > a:before,
+.control-sidebar-menu > li > a:after {
content: " ";
+ display: table;
}
-.user-panel:after {
+.control-sidebar-menu > li > a:after {
clear: both;
}
-.user-panel > .image > img {
- width: 45px;
- height: 45px;
-}
-.user-panel > .info {
- font-weight: 600;
- padding: 5px 5px 5px 15px;
- font-size: 14px;
- line-height: 1;
+.control-sidebar-menu > li > a > .control-sidebar-subheading {
+ margin-top: 0;
}
-.user-panel > .info > p {
- margin-bottom: 9px;
+.control-sidebar-menu .menu-icon {
+ float: left;
+ width: 35px;
+ height: 35px;
+ border-radius: 50%;
+ text-align: center;
+ line-height: 35px;
}
-.user-panel > .info > a {
- text-decoration: none;
- padding-right: 5px;
+.control-sidebar-menu .menu-info {
+ margin-left: 45px;
margin-top: 3px;
+}
+.control-sidebar-menu .menu-info > .control-sidebar-subheading {
+ margin: 0;
+}
+.control-sidebar-menu .menu-info > p {
+ margin: 0;
font-size: 11px;
- font-weight: normal;
}
-.user-panel > .info > a > .fa,
-.user-panel > .info > a > .ion,
-.user-panel > .info > a > .glyphicon {
- margin-right: 3px;
+.control-sidebar-menu .progress {
+ margin: 0;
}
-/*
- * Off Canvas
- * --------------------------------------------------
- * Gives us the push menu effect
- */
-@media screen and (max-width: 992px) {
- .relative {
- position: relative;
- }
- .row-offcanvas-right .sidebar-offcanvas {
- right: -220px;
- }
- .row-offcanvas-left .sidebar-offcanvas {
- left: -220px;
- }
- .row-offcanvas-right.active {
- right: 220px;
- }
- .row-offcanvas-left.active {
- left: 220px;
- }
- .sidebar-offcanvas {
- left: 0;
- }
- body.fixed .sidebar-offcanvas {
- margin-top: 50px;
- left: -220px;
- }
- body.fixed .row-offcanvas-left.active .navbar {
- left: 220px !important;
- right: 0;
- }
- body.fixed .row-offcanvas-left.active .sidebar-offcanvas {
- left: 0px;
- }
+.control-sidebar-dark {
+ color: #b8c7ce;
+}
+.control-sidebar-dark,
+.control-sidebar-dark + .control-sidebar-bg {
+ background: #222d32;
+}
+.control-sidebar-dark .control-sidebar-tabs {
+ border-bottom: #1c2529;
+}
+.control-sidebar-dark .control-sidebar-tabs > li > a {
+ background: #181f23;
+ color: #b8c7ce;
+}
+.control-sidebar-dark .control-sidebar-tabs > li > a,
+.control-sidebar-dark .control-sidebar-tabs > li > a:hover {
+ border-left-color: #141a1d !important;
+ border-bottom-color: #141a1d !important;
+}
+.control-sidebar-dark .control-sidebar-tabs > li > a:hover,
+.control-sidebar-dark .control-sidebar-tabs > li > a:focus,
+.control-sidebar-dark .control-sidebar-tabs > li > a:active {
+ background: #1c2529;
+}
+.control-sidebar-dark .control-sidebar-tabs > li.active > a,
+.control-sidebar-dark .control-sidebar-tabs > li.active > a:hover,
+.control-sidebar-dark .control-sidebar-tabs > li.active > a:focus,
+.control-sidebar-dark .control-sidebar-tabs > li.active > a:active {
+ background: #222d32;
+ color: #fff;
+}
+.control-sidebar-dark .control-sidebar-heading,
+.control-sidebar-dark .control-sidebar-subheading {
+ color: #fff;
+}
+.control-sidebar-dark .control-sidebar-menu > li > a:hover {
+ background: #1e282c;
+}
+.control-sidebar-dark .control-sidebar-menu > li > a .menu-info > p {
+ color: #b8c7ce;
+}
+.control-sidebar-light {
+ color: #5e5e5e;
+}
+.control-sidebar-light,
+.control-sidebar-light + .control-sidebar-bg {
+ background: #f9fafc;
+ border-left: 1px solid #d2d6de;
+}
+.control-sidebar-light .control-sidebar-tabs {
+ border-bottom: #d2d6de;
+}
+.control-sidebar-light .control-sidebar-tabs > li > a {
+ background: #e8ecf4;
+ color: #444444;
+}
+.control-sidebar-light .control-sidebar-tabs > li > a,
+.control-sidebar-light .control-sidebar-tabs > li > a:hover {
+ border-left-color: #d2d6de !important;
+ border-bottom-color: #d2d6de !important;
+}
+.control-sidebar-light .control-sidebar-tabs > li > a:hover,
+.control-sidebar-light .control-sidebar-tabs > li > a:focus,
+.control-sidebar-light .control-sidebar-tabs > li > a:active {
+ background: #eff1f7;
+}
+.control-sidebar-light .control-sidebar-tabs > li.active > a,
+.control-sidebar-light .control-sidebar-tabs > li.active > a:hover,
+.control-sidebar-light .control-sidebar-tabs > li.active > a:focus,
+.control-sidebar-light .control-sidebar-tabs > li.active > a:active {
+ background: #f9fafc;
+ color: #111;
+}
+.control-sidebar-light .control-sidebar-heading,
+.control-sidebar-light .control-sidebar-subheading {
+ color: #111;
+}
+.control-sidebar-light .control-sidebar-menu {
+ margin-left: -14px;
+}
+.control-sidebar-light .control-sidebar-menu > li > a:hover {
+ background: #f4f4f5;
+}
+.control-sidebar-light .control-sidebar-menu > li > a .menu-info > p {
+ color: #5e5e5e;
}
/*
- Dropdown menus
-----------------------------
-*/
+ * Component: Dropdown menus
+ * -------------------------
+ */
/*Dropdowns in general*/
.dropdown-menu {
- -webkit-box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1);
- box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1);
- z-index: 2300;
+ box-shadow: none;
+ border-color: #eee;
+}
+.dropdown-menu > li > a {
+ color: #777;
}
.dropdown-menu > li > a > .glyphicon,
.dropdown-menu > li > a > .fa,
@@ -734,17 +901,21 @@ body > .header .logo .icon {
margin-right: 10px;
}
.dropdown-menu > li > a:hover {
- background-color: #3c8dbc;
- color: #f9f9f9;
+ background-color: #e1e3e9;
+ color: #333;
}
-/*Drodown in navbars*/
-.skin-blue .navbar .dropdown-menu > li > a {
- color: #444444;
+.dropdown-menu > .divider {
+ background-color: #eee;
+}
+.navbar-nav .dropdown-menu {
+ -webkit-box-shadow: none !important;
+ box-shadow: none !important;
+}
+.navbar-nav > .notifications-menu,
+.navbar-nav > .messages-menu,
+.navbar-nav > .tasks-menu {
+ position: relative;
}
-/*
- Navbar custom dropdown menu
-------------------------------------
-*/
.navbar-nav > .notifications-menu > .dropdown-menu,
.navbar-nav > .messages-menu > .dropdown-menu,
.navbar-nav > .tasks-menu > .dropdown-menu {
@@ -752,22 +923,15 @@ body > .header .logo .icon {
padding: 0 0 0 0!important;
margin: 0!important;
top: 100%;
- border: 1px solid #dfdfdf;
- -webkit-border-radius: 4px !important;
- -moz-border-radius: 4px !important;
- border-radius: 4px !important;
+}
+.navbar-nav > .notifications-menu > .dropdown-menu > li,
+.navbar-nav > .messages-menu > .dropdown-menu > li,
+.navbar-nav > .tasks-menu > .dropdown-menu > li {
+ position: relative;
}
.navbar-nav > .notifications-menu > .dropdown-menu > li.header,
.navbar-nav > .messages-menu > .dropdown-menu > li.header,
.navbar-nav > .tasks-menu > .dropdown-menu > li.header {
- -webkit-border-top-left-radius: 4px;
- -webkit-border-top-right-radius: 4px;
- -webkit-border-bottom-right-radius: 0;
- -webkit-border-bottom-left-radius: 0;
- -moz-border-radius-topleft: 4px;
- -moz-border-radius-topright: 4px;
- -moz-border-radius-bottomright: 0;
- -moz-border-radius-bottomleft: 0;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border-bottom-right-radius: 0;
@@ -778,54 +942,38 @@ body > .header .logo .icon {
color: #444444;
font-size: 14px;
}
-.navbar-nav > .notifications-menu > .dropdown-menu > li.header:after,
-.navbar-nav > .messages-menu > .dropdown-menu > li.header:after,
-.navbar-nav > .tasks-menu > .dropdown-menu > li.header:after {
- bottom: 100%;
- left: 92%;
- border: solid transparent;
- content: " ";
- height: 0;
- width: 0;
- position: absolute;
- pointer-events: none;
- border-color: rgba(255, 255, 255, 0);
- border-bottom-color: #ffffff;
- border-width: 7px;
- margin-left: -7px;
-}
.navbar-nav > .notifications-menu > .dropdown-menu > li.footer > a,
.navbar-nav > .messages-menu > .dropdown-menu > li.footer > a,
.navbar-nav > .tasks-menu > .dropdown-menu > li.footer > a {
- -webkit-border-top-left-radius: 0px;
- -webkit-border-top-right-radius: 0px;
- -webkit-border-bottom-right-radius: 4px;
- -webkit-border-bottom-left-radius: 4px;
- -moz-border-radius-topleft: 0px;
- -moz-border-radius-topright: 0px;
- -moz-border-radius-bottomright: 4px;
- -moz-border-radius-bottomleft: 4px;
border-top-left-radius: 0px;
border-top-right-radius: 0px;
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
font-size: 12px;
- background-color: #f4f4f4;
+ background-color: #fff;
padding: 7px 10px;
border-bottom: 1px solid #eeeeee;
- color: #444444;
+ color: #444!important;
text-align: center;
}
+@media (max-width: 991px) {
+ .navbar-nav > .notifications-menu > .dropdown-menu > li.footer > a,
+ .navbar-nav > .messages-menu > .dropdown-menu > li.footer > a,
+ .navbar-nav > .tasks-menu > .dropdown-menu > li.footer > a {
+ background: #fff!important;
+ color: #444!important;
+ }
+}
.navbar-nav > .notifications-menu > .dropdown-menu > li.footer > a:hover,
.navbar-nav > .messages-menu > .dropdown-menu > li.footer > a:hover,
.navbar-nav > .tasks-menu > .dropdown-menu > li.footer > a:hover {
- background: #f4f4f4;
text-decoration: none;
font-weight: normal;
}
.navbar-nav > .notifications-menu > .dropdown-menu > li .menu,
.navbar-nav > .messages-menu > .dropdown-menu > li .menu,
.navbar-nav > .tasks-menu > .dropdown-menu > li .menu {
+ max-height: 200px;
margin: 0;
padding: 0;
list-style: none;
@@ -842,71 +990,43 @@ body > .header .logo .icon {
.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a:hover,
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a:hover,
.navbar-nav > .tasks-menu > .dropdown-menu > li .menu > li > a:hover {
- background: #f6f6f6;
+ background: #f4f4f4;
text-decoration: none;
}
.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a {
- font-size: 12px;
color: #444444;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ padding: 10px;
}
.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .glyphicon,
.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .fa,
.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .ion {
- font-size: 20px;
- width: 50px;
- text-align: center;
- padding: 15px 0px;
- margin-right: 5px;
- /* Default background and font colors */
- background: #00c0ef;
- color: #f9f9f9;
- /* Fallback for browsers that doesn't support rgba */
- color: rgba(255, 255, 255, 0.7);
-}
-.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .glyphicon.danger,
-.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .fa.danger,
-.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .ion.danger {
- background: #f56954;
-}
-.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .glyphicon.warning,
-.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .fa.warning,
-.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .ion.warning {
- background: #f39c12;
-}
-.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .glyphicon.success,
-.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .fa.success,
-.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .ion.success {
- background: #00a65a;
-}
-.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .glyphicon.info,
-.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .fa.info,
-.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .ion.info {
- background: #00c0ef;
+ width: 20px;
}
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a {
margin: 0px;
- line-height: 20px;
- padding: 10px 5px 10px 5px;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
+ padding: 10px 10px;
}
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a > div > img {
margin: auto 10px auto auto;
width: 40px;
height: 40px;
- border: 1px solid #dddddd;
}
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a > h4 {
padding: 0;
margin: 0 0 0 45px;
color: #444444;
font-size: 15px;
+ position: relative;
}
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a > h4 > small {
color: #999999;
font-size: 10px;
- float: right;
+ position: absolute;
+ top: 0px;
+ right: 0px;
}
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a > p {
margin: 0 0 0 45px;
@@ -915,8 +1035,8 @@ body > .header .logo .icon {
}
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a:before,
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a:after {
- display: table;
content: " ";
+ display: table;
}
.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a:after {
clear: both;
@@ -935,180 +1055,196 @@ body > .header .logo .icon {
margin: 0;
}
.navbar-nav > .user-menu > .dropdown-menu {
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
padding: 1px 0 0 0;
border-top-width: 0;
width: 280px;
}
-.navbar-nav > .user-menu > .dropdown-menu:after {
- bottom: 100%;
- right: 10px;
- border: solid transparent;
- content: " ";
- height: 0;
- width: 0;
- position: absolute;
- pointer-events: none;
- border-color: rgba(255, 255, 255, 0);
- border-bottom-color: #ffffff;
- border-width: 10px;
- margin-left: -10px;
+.navbar-nav > .user-menu > .dropdown-menu,
+.navbar-nav > .user-menu > .dropdown-menu > .user-body {
+ border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px;
}
.navbar-nav > .user-menu > .dropdown-menu > li.user-header {
height: 175px;
padding: 10px;
- background: #3c8dbc;
text-align: center;
}
.navbar-nav > .user-menu > .dropdown-menu > li.user-header > img {
z-index: 5;
height: 90px;
width: 90px;
- border: 8px solid;
+ border: 3px solid;
border-color: transparent;
border-color: rgba(255, 255, 255, 0.2);
}
.navbar-nav > .user-menu > .dropdown-menu > li.user-header > p {
z-index: 5;
- color: #f9f9f9;
+ color: #fff;
color: rgba(255, 255, 255, 0.8);
font-size: 17px;
- text-shadow: 2px 2px 3px #333333;
margin-top: 10px;
}
.navbar-nav > .user-menu > .dropdown-menu > li.user-header > p > small {
display: block;
font-size: 12px;
}
-.navbar-nav > .user-menu > .dropdown-menu > li.user-body {
+.navbar-nav > .user-menu > .dropdown-menu > .user-body {
padding: 15px;
border-bottom: 1px solid #f4f4f4;
border-top: 1px solid #dddddd;
}
-.navbar-nav > .user-menu > .dropdown-menu > li.user-body:before,
-.navbar-nav > .user-menu > .dropdown-menu > li.user-body:after {
- display: table;
+.navbar-nav > .user-menu > .dropdown-menu > .user-body:before,
+.navbar-nav > .user-menu > .dropdown-menu > .user-body:after {
content: " ";
+ display: table;
}
-.navbar-nav > .user-menu > .dropdown-menu > li.user-body:after {
+.navbar-nav > .user-menu > .dropdown-menu > .user-body:after {
clear: both;
}
-.navbar-nav > .user-menu > .dropdown-menu > li.user-body > div > a {
- color: #0073b7;
+.navbar-nav > .user-menu > .dropdown-menu > .user-body a {
+ color: #444 !important;
+}
+@media (max-width: 991px) {
+ .navbar-nav > .user-menu > .dropdown-menu > .user-body a {
+ background: #fff !important;
+ color: #444 !important;
+ }
}
-.navbar-nav > .user-menu > .dropdown-menu > li.user-footer {
+.navbar-nav > .user-menu > .dropdown-menu > .user-footer {
background-color: #f9f9f9;
padding: 10px;
}
-.navbar-nav > .user-menu > .dropdown-menu > li.user-footer:before,
-.navbar-nav > .user-menu > .dropdown-menu > li.user-footer:after {
- display: table;
+.navbar-nav > .user-menu > .dropdown-menu > .user-footer:before,
+.navbar-nav > .user-menu > .dropdown-menu > .user-footer:after {
content: " ";
+ display: table;
}
-.navbar-nav > .user-menu > .dropdown-menu > li.user-footer:after {
+.navbar-nav > .user-menu > .dropdown-menu > .user-footer:after {
clear: both;
}
-.navbar-nav > .user-menu > .dropdown-menu > li.user-footer .btn-default {
+.navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default {
color: #666666;
}
-/* Add fade animation to dropdown menus */
-.open > .dropdown-menu {
- animation-name: fadeAnimation;
- animation-duration: .7s;
- animation-iteration-count: 1;
- animation-timing-function: ease;
- animation-fill-mode: forwards;
- -webkit-animation-name: fadeAnimation;
- -webkit-animation-duration: .7s;
- -webkit-animation-iteration-count: 1;
- -webkit-animation-timing-function: ease;
- -webkit-animation-fill-mode: forwards;
- -moz-animation-name: fadeAnimation;
- -moz-animation-duration: .7s;
- -moz-animation-iteration-count: 1;
- -moz-animation-timing-function: ease;
- -moz-animation-fill-mode: forwards;
-}
-@keyframes fadeAnimation {
- from {
+.navbar-nav > .user-menu .user-image {
+ float: left;
+ width: 25px;
+ height: 25px;
+ border-radius: 50%;
+ margin-right: 10px;
+ margin-top: -2px;
+}
+@media (max-width: 767px) {
+ .navbar-nav > .user-menu .user-image {
+ float: none;
+ margin-right: 0;
+ margin-top: -8px;
+ line-height: 10px;
+ }
+}
+/* Add fade animation to dropdown menus by appending
+ the class .animated-dropdown-menu to the .dropdown-menu ul (or ol)*/
+.open:not(.dropup) > .animated-dropdown-menu {
+ backface-visibility: visible !important;
+ -webkit-animation: flipInX 0.7s both;
+ -o-animation: flipInX 0.7s both;
+ animation: flipInX 0.7s both;
+}
+@keyframes flipInX {
+ 0% {
+ transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+ transition-timing-function: ease-in;
opacity: 0;
- top: 120%;
}
- to {
+ 40% {
+ transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+ transition-timing-function: ease-in;
+ }
+ 60% {
+ transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
opacity: 1;
- top: 100%;
+ }
+ 80% {
+ transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
+ }
+ 100% {
+ transform: perspective(400px);
}
}
-@-webkit-keyframes fadeAnimation {
- from {
+@-webkit-keyframes flipInX {
+ 0% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+ -webkit-transition-timing-function: ease-in;
opacity: 0;
- top: 120%;
}
- to {
- opacity: 1;
- top: 100%;
+ 40% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+ -webkit-transition-timing-function: ease-in;
}
-}
-/* Fix dropdown menu for small screens to display correctly on small screens */
-@media screen and (max-width: 767px) {
- .navbar-nav > .notifications-menu > .dropdown-menu,
- .navbar-nav > .user-menu > .dropdown-menu,
- .navbar-nav > .tasks-menu > .dropdown-menu,
- .navbar-nav > .messages-menu > .dropdown-menu {
- position: absolute;
- top: 100%;
- right: 0;
- left: auto;
- border-right: 1px solid #dddddd;
- border-bottom: 1px solid #dddddd;
- border-left: 1px solid #dddddd;
- background: #ffffff;
+ 60% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
+ opacity: 1;
}
-}
-/* Fix menu positions on xs screens to appear correctly and fully */
-@media screen and (max-width: 480px) {
- .navbar-nav > .notifications-menu > .dropdown-menu > li.header,
- .navbar-nav > .tasks-menu > .dropdown-menu > li.header,
- .navbar-nav > .messages-menu > .dropdown-menu > li.header {
- /* Remove arrow from the top */
+ 80% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
}
- .navbar-nav > .notifications-menu > .dropdown-menu > li.header:after,
- .navbar-nav > .tasks-menu > .dropdown-menu > li.header:after,
- .navbar-nav > .messages-menu > .dropdown-menu > li.header:after {
- border-width: 0px!important;
+ 100% {
+ -webkit-transform: perspective(400px);
}
- .navbar-nav > .tasks-menu > .dropdown-menu {
- position: absolute;
- right: -120px;
- left: auto;
+}
+/* Fix dropdown menu in navbars */
+.navbar-custom-menu > .navbar-nav > li {
+ position: relative;
+}
+.navbar-custom-menu > .navbar-nav > li > .dropdown-menu {
+ position: absolute;
+ right: 0;
+ left: auto;
+}
+@media (max-width: 991px) {
+ .navbar-custom-menu > .navbar-nav {
+ float: right;
}
- .navbar-nav > .notifications-menu > .dropdown-menu {
- position: absolute;
- right: -170px;
- left: auto;
+ .navbar-custom-menu > .navbar-nav > li {
+ position: static;
}
- .navbar-nav > .messages-menu > .dropdown-menu {
+ .navbar-custom-menu > .navbar-nav > li > .dropdown-menu {
position: absolute;
- right: -210px;
+ right: 5%;
left: auto;
+ border: 1px solid #ddd;
+ background: #fff;
}
}
/*
- All form elements including input, select, textarea etc.
------------------------------------------------------------------
-*/
+ * Component: Form
+ * ---------------
+ */
.form-control {
- -webkit-border-radius: 0px !important;
- -moz-border-radius: 0px !important;
border-radius: 0px !important;
box-shadow: none;
+ border-color: #d2d6de;
}
.form-control:focus {
border-color: #3c8dbc !important;
box-shadow: none;
}
+.form-control::-moz-placeholder {
+ color: #bbb;
+ opacity: 1;
+}
+.form-control:-ms-input-placeholder {
+ color: #bbb;
+}
+.form-control::-webkit-input-placeholder {
+ color: #bbb;
+}
+.form-control:not(select) {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+}
.form-group.has-success label {
color: #00a65a;
}
@@ -1124,38 +1260,72 @@ body > .header .logo .icon {
box-shadow: none;
}
.form-group.has-error label {
- color: #f56954;
+ color: #dd4b39;
}
.form-group.has-error .form-control {
- border-color: #f56954 !important;
+ border-color: #dd4b39 !important;
box-shadow: none;
}
/* Input group */
.input-group .input-group-addon {
- border-radius: 0;
- background-color: #f4f4f4;
+ border-radius: 0px;
+ border-color: #d2d6de;
+ background-color: #fff;
}
/* button groups */
.btn-group-vertical .btn.btn-flat:first-of-type,
.btn-group-vertical .btn.btn-flat:last-of-type {
border-radius: 0;
}
-/* Checkbox and radio inputs */
-.checkbox,
-.radio {
+.icheck > label {
padding-left: 0;
}
/*
- Compenent: Progress bars
---------------------------------
-*/
+ * Component: Progress Bar
+ * -----------------------
+ */
+.progress,
+.progress > .progress-bar {
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+.progress,
+.progress > .progress-bar,
+.progress .progress-bar,
+.progress > .progress-bar .progress-bar {
+ border-radius: 1px;
+}
/* size variation */
-.progress.sm {
+.progress.sm,
+.progress-sm {
height: 10px;
}
-.progress.xs {
+.progress.sm,
+.progress-sm,
+.progress.sm .progress-bar,
+.progress-sm .progress-bar {
+ border-radius: 1px;
+}
+.progress.xs,
+.progress-xs {
height: 7px;
}
+.progress.xs,
+.progress-xs,
+.progress.xs .progress-bar,
+.progress-xs .progress-bar {
+ border-radius: 1px;
+}
+.progress.xxs,
+.progress-xxs {
+ height: 3px;
+}
+.progress.xxs,
+.progress-xxs,
+.progress.xxs .progress-bar,
+.progress-xxs .progress-bar {
+ border-radius: 1px;
+}
/* Vertical bars */
.progress.vertical {
position: relative;
@@ -1169,12 +1339,24 @@ body > .header .logo .icon {
position: absolute;
bottom: 0;
}
-.progress.vertical.sm {
+.progress.vertical.sm,
+.progress.vertical.progress-sm {
width: 20px;
}
-.progress.vertical.xs {
+.progress.vertical.xs,
+.progress.vertical.progress-xs {
width: 10px;
}
+.progress.vertical.xxs,
+.progress.vertical.progress-xxs {
+ width: 3px;
+}
+.progress-group .progress-text {
+ font-weight: 600;
+}
+.progress-group .progress-number {
+ float: right;
+}
/* Remove margins from progress bars when put in a table */
.table tr > td .progress {
margin: 0;
@@ -1185,9 +1367,8 @@ body > .header .logo .icon {
}
.progress-striped .progress-bar-light-blue,
.progress-striped .progress-bar-primary {
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
.progress-bar-green,
@@ -1196,9 +1377,8 @@ body > .header .logo .icon {
}
.progress-striped .progress-bar-green,
.progress-striped .progress-bar-success {
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
.progress-bar-aqua,
@@ -1207,9 +1387,8 @@ body > .header .logo .icon {
}
.progress-striped .progress-bar-aqua,
.progress-striped .progress-bar-info {
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
.progress-bar-yellow,
@@ -1218,32 +1397,30 @@ body > .header .logo .icon {
}
.progress-striped .progress-bar-yellow,
.progress-striped .progress-bar-warning {
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
.progress-bar-red,
.progress-bar-danger {
- background-color: #f56954;
+ background-color: #dd4b39;
}
.progress-striped .progress-bar-red,
.progress-striped .progress-bar-danger {
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
/*
- Component: Small boxes
-*/
+ * Component: Small Box
+ * --------------------
+ */
.small-box {
+ border-radius: 2px;
position: relative;
display: block;
- -webkit-border-radius: 2px;
- -moz-border-radius: 2px;
- border-radius: 2px;
- margin-bottom: 15px;
+ margin-bottom: 20px;
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
.small-box > .inner {
padding: 10px;
@@ -1284,10 +1461,12 @@ body > .header .logo .icon {
z-index: 5px;
}
.small-box .icon {
+ -webkit-transition: all 0.3s linear;
+ -o-transition: all 0.3s linear;
+ transition: all 0.3s linear;
position: absolute;
- top: auto;
- bottom: 5px;
- right: 5px;
+ top: -10px;
+ right: 10px;
z-index: 0;
font-size: 90px;
color: rgba(0, 0, 0, 0.15);
@@ -1297,39 +1476,9 @@ body > .header .logo .icon {
color: #f9f9f9;
}
.small-box:hover .icon {
- animation-name: tansformAnimation;
- animation-duration: .5s;
- animation-iteration-count: 1;
- animation-timing-function: ease;
- animation-fill-mode: forwards;
- -webkit-animation-name: tansformAnimation;
- -webkit-animation-duration: .5s;
- -webkit-animation-iteration-count: 1;
- -webkit-animation-timing-function: ease;
- -webkit-animation-fill-mode: forwards;
- -moz-animation-name: tansformAnimation;
- -moz-animation-duration: .5s;
- -moz-animation-iteration-count: 1;
- -moz-animation-timing-function: ease;
- -moz-animation-fill-mode: forwards;
-}
-@keyframes tansformAnimation {
- from {
- font-size: 90px;
- }
- to {
- font-size: 100px;
- }
-}
-@-webkit-keyframes tansformAnimation {
- from {
- font-size: 90px;
- }
- to {
- font-size: 100px;
- }
-}
-@media screen and (max-width: 480px) {
+ font-size: 95px;
+}
+@media (max-width: 767px) {
.small-box {
text-align: center;
}
@@ -1341,19 +1490,17 @@ body > .header .logo .icon {
}
}
/*
- component: Boxes
--------------------------
-*/
+ * Component: Box
+ * --------------
+ */
.box {
position: relative;
+ border-radius: 3px;
background: #ffffff;
- border-top: 2px solid #c1c1c1;
+ border-top: 3px solid #d2d6de;
margin-bottom: 20px;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
width: 100%;
- box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
.box.box-primary {
border-top-color: #3c8dbc;
@@ -1362,7 +1509,7 @@ body > .header .logo .icon {
border-top-color: #00c0ef;
}
.box.box-danger {
- border-top-color: #f56954;
+ border-top-color: #dd4b39;
}
.box.box-warning {
border-top-color: #f39c12;
@@ -1370,201 +1517,116 @@ body > .header .logo .icon {
.box.box-success {
border-top-color: #00a65a;
}
-.box.height-control .box-body {
- max-height: 300px;
- overflow: auto;
-}
-.box .box-header {
- position: relative;
- -webkit-border-top-left-radius: 3px;
- -webkit-border-top-right-radius: 3px;
- -webkit-border-bottom-right-radius: 0;
- -webkit-border-bottom-left-radius: 0;
- -moz-border-radius-topleft: 3px;
- -moz-border-radius-topright: 3px;
- -moz-border-radius-bottomright: 0;
- -moz-border-radius-bottomleft: 0;
- border-top-left-radius: 3px;
- border-top-right-radius: 3px;
- border-bottom-right-radius: 0;
- border-bottom-left-radius: 0;
- border-bottom: 0px solid #f4f4f4;
- color: #444;
-}
-.box .box-header:before,
-.box .box-header:after {
- display: table;
- content: " ";
+.box.box-default {
+ border-top-color: #d2d6de;
}
-.box .box-header:after {
- clear: both;
+.box.collapsed-box .box-body,
+.box.collapsed-box .box-footer {
+ display: none;
}
-.box .box-header > .fa,
-.box .box-header > .glyphicon,
-.box .box-header > .ion,
-.box .box-header .box-title {
- display: inline-block;
- padding: 10px 10px 10px 10px;
+.box .nav-stacked > li {
+ border-bottom: 1px solid #f4f4f4;
margin: 0;
- font-size: 20px;
- font-weight: 400;
- float: left;
- cursor: default;
-}
-.box .box-header a {
- color: #444;
-}
-.box .box-header > .box-tools {
- padding: 5px 10px 5px 5px;
-}
-.box .box-body {
- padding: 10px;
- -webkit-border-top-left-radius: 0;
- -webkit-border-top-right-radius: 0;
- -webkit-border-bottom-right-radius: 3px;
- -webkit-border-bottom-left-radius: 3px;
- -moz-border-radius-topleft: 0;
- -moz-border-radius-topright: 0;
- -moz-border-radius-bottomright: 3px;
- -moz-border-radius-bottomleft: 3px;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
- border-bottom-right-radius: 3px;
- border-bottom-left-radius: 3px;
-}
-.box .box-body > table,
-.box .box-body > .table {
- margin-bottom: 0;
-}
-.box .box-body.chart-responsive {
- width: 100%;
- overflow: hidden;
-}
-.box .box-body > .chart {
- position: relative;
- overflow: hidden;
- width: 100%;
-}
-.box .box-body > .chart svg,
-.box .box-body > .chart canvas {
- width: 100%!important;
-}
-.box .box-body .fc {
- margin-top: 5px;
-}
-.box .box-body .fc-header-title h2 {
- font-size: 15px;
- line-height: 1.6em;
- color: #666;
- margin-left: 10px;
-}
-.box .box-body .fc-header-right {
- padding-right: 10px;
-}
-.box .box-body .fc-header-left {
- padding-left: 10px;
}
-.box .box-body .fc-widget-header {
- background: #fafafa;
- box-shadow: inset 0px -3px 1px rgba(0, 0, 0, 0.02);
+.box .nav-stacked > li:last-of-type {
+ border-bottom: none;
}
-.box .box-body .fc-grid {
- width: 100%;
- border: 0;
+.box.height-control .box-body {
+ max-height: 300px;
+ overflow: auto;
}
-.box .box-body .fc-widget-header:first-of-type,
-.box .box-body .fc-widget-content:first-of-type {
- border-left: 0;
- border-right: 0;
+.box .border-right {
+ border-right: 1px solid #f4f4f4;
}
-.box .box-body .fc-widget-header:last-of-type,
-.box .box-body .fc-widget-content:last-of-type {
- border-right: 0;
+.box .border-left {
+ border-left: 1px solid #f4f4f4;
}
-.box .box-body .table {
- margin-bottom: 0;
+.box.box-solid {
+ border-top: 0px;
}
-.box .box-body .full-width-chart {
- margin: -19px;
+.box.box-solid > .box-header .btn.btn-default {
+ background: transparent;
}
-.box .box-body.no-padding .full-width-chart {
- margin: -9px;
+.box.box-solid > .box-header .btn:hover,
+.box.box-solid > .box-header a:hover {
+ background: rgba(0, 0, 0, 0.1) !important;
}
-.box .box-footer {
- border-top: 1px solid #f4f4f4;
- -webkit-border-top-left-radius: 0;
- -webkit-border-top-right-radius: 0;
- -webkit-border-bottom-right-radius: 3px;
- -webkit-border-bottom-left-radius: 3px;
- -moz-border-radius-topleft: 0;
- -moz-border-radius-topright: 0;
- -moz-border-radius-bottomright: 3px;
- -moz-border-radius-bottomleft: 3px;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
- border-bottom-right-radius: 3px;
- border-bottom-left-radius: 3px;
- padding: 10px;
- background-color: #ffffff;
+.box.box-solid.box-default {
+ border: 1px solid #d2d6de;
}
-.box.box-solid {
- border-top: 0px;
+.box.box-solid.box-default > .box-header {
+ color: #444444;
+ background: #d2d6de;
+ background-color: #d2d6de;
}
-.box.box-solid > .box-header {
- padding-bottom: 0px!important;
+.box.box-solid.box-default > .box-header a,
+.box.box-solid.box-default > .box-header .btn {
+ color: #444444;
}
-.box.box-solid > .box-header .btn.btn-default {
- background: transparent;
+.box.box-solid.box-primary {
+ border: 1px solid #3c8dbc;
}
.box.box-solid.box-primary > .box-header {
- color: #fff;
+ color: #ffffff;
background: #3c8dbc;
background-color: #3c8dbc;
}
-.box.box-solid.box-primary > .box-header a {
- color: #444;
+.box.box-solid.box-primary > .box-header a,
+.box.box-solid.box-primary > .box-header .btn {
+ color: #ffffff;
+}
+.box.box-solid.box-info {
+ border: 1px solid #00c0ef;
}
.box.box-solid.box-info > .box-header {
- color: #fff;
+ color: #ffffff;
background: #00c0ef;
background-color: #00c0ef;
}
-.box.box-solid.box-info > .box-header a {
- color: #444;
+.box.box-solid.box-info > .box-header a,
+.box.box-solid.box-info > .box-header .btn {
+ color: #ffffff;
+}
+.box.box-solid.box-danger {
+ border: 1px solid #dd4b39;
}
.box.box-solid.box-danger > .box-header {
- color: #fff;
- background: #f56954;
- background-color: #f56954;
+ color: #ffffff;
+ background: #dd4b39;
+ background-color: #dd4b39;
}
-.box.box-solid.box-danger > .box-header a {
- color: #444;
+.box.box-solid.box-danger > .box-header a,
+.box.box-solid.box-danger > .box-header .btn {
+ color: #ffffff;
+}
+.box.box-solid.box-warning {
+ border: 1px solid #f39c12;
}
.box.box-solid.box-warning > .box-header {
- color: #fff;
+ color: #ffffff;
background: #f39c12;
background-color: #f39c12;
}
-.box.box-solid.box-warning > .box-header a {
- color: #444;
+.box.box-solid.box-warning > .box-header a,
+.box.box-solid.box-warning > .box-header .btn {
+ color: #ffffff;
+}
+.box.box-solid.box-success {
+ border: 1px solid #00a65a;
}
.box.box-solid.box-success > .box-header {
- color: #fff;
+ color: #ffffff;
background: #00a65a;
background-color: #00a65a;
}
-.box.box-solid.box-success > .box-header a {
- color: #444;
+.box.box-solid.box-success > .box-header a,
+.box.box-solid.box-success > .box-header .btn {
+ color: #ffffff;
}
.box.box-solid > .box-header > .box-tools .btn {
border: 0;
box-shadow: none;
}
-.box.box-solid.collapsed-box .box-header {
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
.box.box-solid[class*='bg'] > .box-header {
color: #fff;
}
@@ -1578,168 +1640,369 @@ body > .header .logo .icon {
font-size: 12px;
margin-bottom: 0.3em;
}
-.box .todo-list {
+.box > .overlay,
+.overlay-wrapper > .overlay,
+.box > .loading-img,
+.overlay-wrapper > .loading-img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+.box .overlay,
+.overlay-wrapper .overlay {
+ z-index: 50;
+ background: rgba(255, 255, 255, 0.7);
+ border-radius: 3px;
+}
+.box .overlay > .fa,
+.overlay-wrapper .overlay > .fa {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin-left: -15px;
+ margin-top: -15px;
+ color: #000;
+ font-size: 30px;
+}
+.box .overlay.dark,
+.overlay-wrapper .overlay.dark {
+ background: rgba(0, 0, 0, 0.5);
+}
+.box-header:before,
+.box-body:before,
+.box-footer:before,
+.box-header:after,
+.box-body:after,
+.box-footer:after {
+ content: " ";
+ display: table;
+}
+.box-header:after,
+.box-body:after,
+.box-footer:after {
+ clear: both;
+}
+.box-header {
+ color: #444;
+ display: block;
+ padding: 10px;
+ position: relative;
+}
+.box-header.with-border {
+ border-bottom: 1px solid #f4f4f4;
+}
+.collapsed-box .box-header.with-border {
+ border-bottom: none;
+}
+.box-header > .fa,
+.box-header > .glyphicon,
+.box-header > .ion,
+.box-header .box-title {
+ display: inline-block;
+ font-size: 18px;
+ margin: 0;
+ line-height: 1;
+}
+.box-header > .fa,
+.box-header > .glyphicon,
+.box-header > .ion {
+ margin-right: 5px;
+}
+.box-header > .box-tools {
+ position: absolute;
+ right: 10px;
+ top: 5px;
+}
+.box-header > .box-tools [data-toggle="tooltip"] {
+ position: relative;
+}
+.box-header > .box-tools.pull-right .dropdown-menu {
+ right: 0;
+ left: auto;
+}
+.btn-box-tool {
+ padding: 5px;
+ font-size: 12px;
+ background: transparent;
+ box-shadow: none!important;
+ color: #97a0b3;
+}
+.open .btn-box-tool,
+.btn-box-tool:hover {
+ color: #606c84;
+}
+.btn-box-tool:active {
+ outline: none!important;
+}
+.box-body {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
+ padding: 10px;
+}
+.no-header .box-body {
+ border-top-right-radius: 3px;
+ border-top-left-radius: 3px;
+}
+.box-body > .table {
+ margin-bottom: 0;
+}
+.box-body .fc {
+ margin-top: 5px;
+}
+.box-body .full-width-chart {
+ margin: -19px;
+}
+.box-body.no-padding .full-width-chart {
+ margin: -9px;
+}
+.box-body .box-pane {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 3px;
+}
+.box-body .box-pane-right {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 0;
+}
+.box-footer {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
+ border-top: 1px solid #f4f4f4;
+ padding: 10px;
+ background-color: #ffffff;
+}
+.chart-legend {
+ margin: 10px 0;
+}
+@media (max-width: 991px) {
+ .chart-legend > li {
+ float: left;
+ margin-right: 10px;
+ }
+}
+/* Widget: TODO LIST */
+.todo-list {
margin: 0;
padding: 0px 0px;
list-style: none;
+ overflow: auto;
}
-.box .todo-list > li {
- -webkit-border-radius: 2px;
- -moz-border-radius: 2px;
+.todo-list > li {
border-radius: 2px;
padding: 10px;
- background: #f3f4f5;
+ background: #f4f4f4;
margin-bottom: 2px;
border-left: 2px solid #e6e7e8;
color: #444;
}
-.box .todo-list > li:last-of-type {
+.todo-list > li:last-of-type {
margin-bottom: 0;
}
-.box .todo-list > li.danger {
- border-left-color: #f56954;
+.todo-list > li.danger {
+ border-left-color: #dd4b39;
}
-.box .todo-list > li.warning {
+.todo-list > li.warning {
border-left-color: #f39c12;
}
-.box .todo-list > li.info {
+.todo-list > li.info {
border-left-color: #00c0ef;
}
-.box .todo-list > li.success {
+.todo-list > li.success {
border-left-color: #00a65a;
}
-.box .todo-list > li.primary {
+.todo-list > li.primary {
border-left-color: #3c8dbc;
}
-.box .todo-list > li > input[type='checkbox'] {
+.todo-list > li > input[type='checkbox'] {
margin: 0 10px 0 5px;
}
-.box .todo-list > li .text {
+.todo-list > li .text {
display: inline-block;
margin-left: 5px;
font-weight: 600;
}
-.box .todo-list > li .label {
+.todo-list > li .label {
margin-left: 10px;
font-size: 9px;
}
-.box .todo-list > li .tools {
+.todo-list > li .tools {
display: none;
float: right;
- color: #f56954;
+ color: #dd4b39;
}
-.box .todo-list > li .tools > .fa,
-.box .todo-list > li .tools > .glyphicon,
-.box .todo-list > li .tools > .ion {
+.todo-list > li .tools > .fa,
+.todo-list > li .tools > .glyphicon,
+.todo-list > li .tools > .ion {
margin-right: 5px;
cursor: pointer;
}
-.box .todo-list > li:hover .tools {
+.todo-list > li:hover .tools {
display: inline-block;
}
-.box .todo-list > li.done {
+.todo-list > li.done {
color: #999;
}
-.box .todo-list > li.done .text {
+.todo-list > li.done .text {
text-decoration: line-through;
font-weight: 500;
}
-.box .todo-list > li.done .label {
- background: #eaeaec !important;
+.todo-list > li.done .label {
+ background: #d2d6de !important;
}
-.box .todo-list .handle {
+.todo-list .handle {
display: inline-block;
cursor: move;
margin: 0 5px;
}
-.box .chat {
+/* Chat widget (DEPRECATED - this will be removed in the next major release. Use Direct Chat instead)*/
+.chat {
padding: 5px 20px 5px 10px;
}
-.box .chat .item {
+.chat .item {
margin-bottom: 10px;
}
-.box .chat .item:before,
-.box .chat .item:after {
- display: table;
+.chat .item:before,
+.chat .item:after {
content: " ";
+ display: table;
}
-.box .chat .item:after {
+.chat .item:after {
clear: both;
}
-.box .chat .item > img {
+.chat .item > img {
width: 40px;
height: 40px;
border: 2px solid transparent;
- -webkit-border-radius: 50% !important;
- -moz-border-radius: 50% !important;
border-radius: 50% !important;
}
-.box .chat .item > img.online {
+.chat .item > img.online {
border: 2px solid #00a65a;
}
-.box .chat .item > img.offline {
- border: 2px solid #f56954;
+.chat .item > img.offline {
+ border: 2px solid #dd4b39;
}
-.box .chat .item > .message {
+.chat .item > .message {
margin-left: 55px;
margin-top: -40px;
}
-.box .chat .item > .message > .name {
+.chat .item > .message > .name {
display: block;
font-weight: 600;
}
-.box .chat .item > .attachment {
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
+.chat .item > .attachment {
border-radius: 3px;
- background: #f0f0f0;
+ background: #f4f4f4;
margin-left: 65px;
margin-right: 15px;
padding: 10px;
}
-.box .chat .item > .attachment > h4 {
+.chat .item > .attachment > h4 {
margin: 0 0 5px 0;
font-weight: 600;
font-size: 14px;
}
-.box .chat .item > .attachment > p,
-.box .chat .item > .attachment > .filename {
+.chat .item > .attachment > p,
+.chat .item > .attachment > .filename {
font-weight: 600;
font-size: 13px;
font-style: italic;
margin: 0;
}
-.box .chat .item > .attachment:before,
-.box .chat .item > .attachment:after {
- display: table;
+.chat .item > .attachment:before,
+.chat .item > .attachment:after {
content: " ";
+ display: table;
}
-.box .chat .item > .attachment:after {
+.chat .item > .attachment:after {
clear: both;
}
-.box > .overlay,
-.box > .loading-img {
- position: absolute;
- top: 0;
- left: 0;
+.box-input {
+ max-width: 200px;
+}
+.modal .panel-body {
+ color: #444;
+}
+/*
+ * Component: Info Box
+ * -------------------
+ */
+.info-box {
+ display: block;
+ min-height: 90px;
+ background: #fff;
width: 100%;
- height: 100%;
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+ border-radius: 2px;
+ margin-bottom: 15px;
}
-.box > .overlay {
- z-index: 1010;
- background: rgba(255, 255, 255, 0.7);
+.info-box small {
+ font-size: 14px;
}
-.box > .overlay.dark {
- background: rgba(0, 0, 0, 0.5);
+.info-box .progress {
+ background: rgba(0, 0, 0, 0.2);
+ margin: 5px -10px 5px -10px;
+ height: 2px;
+}
+.info-box .progress,
+.info-box .progress .progress-bar {
+ border-radius: 0;
+}
+.info-box .progress .progress-bar {
+ background: #fff;
+}
+.info-box-icon {
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+ display: block;
+ float: left;
+ height: 90px;
+ width: 90px;
+ text-align: center;
+ font-size: 45px;
+ line-height: 90px;
+ background: rgba(0, 0, 0, 0.2);
+}
+.info-box-content {
+ padding: 5px 10px;
+ margin-left: 90px;
+}
+.info-box-number {
+ display: block;
+ font-weight: bold;
+ font-size: 18px;
}
-.box > .loading-img {
- z-index: 1020;
- background: transparent url('../img/ajax-loader1.gif') 50% 50% no-repeat;
+.progress-description,
+.info-box-text {
+ display: block;
+ font-size: 14px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.info-box-text {
+ text-transform: uppercase;
+}
+.info-box-more {
+ display: block;
+}
+.progress-description {
+ margin: 0;
}
/*
-Component: timeline
---------------------
-*/
+ * Component: Timeline
+ * -------------------
+ */
.timeline {
position: relative;
margin: 0 0 30px 0;
@@ -1751,13 +2014,10 @@ Component: timeline
position: absolute;
top: 0px;
bottom: 0;
- width: 5px;
+ width: 4px;
background: #ddd;
- left: 30px;
- border: 1px solid #eee;
+ left: 31px;
margin: 0;
- -webkit-border-radius: 2px;
- -moz-border-radius: 2px;
border-radius: 2px;
}
.timeline > li {
@@ -1767,33 +2027,35 @@ Component: timeline
}
.timeline > li:before,
.timeline > li:after {
- display: table;
content: " ";
+ display: table;
}
.timeline > li:after {
clear: both;
}
.timeline > li > .timeline-item {
- margin-top: 10px;
- border: 0px solid #dfdfdf;
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+ border-radius: 3px;
+ margin-top: 0px;
background: #fff;
- color: #555;
+ color: #444;
margin-left: 60px;
margin-right: 15px;
- padding: 5px;
+ padding: 0;
position: relative;
- box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
}
.timeline > li > .timeline-item > .time {
color: #999;
float: right;
- margin: 2px 0 0 0;
+ padding: 10px;
+ font-size: 12px;
}
.timeline > li > .timeline-item > .timeline-header {
margin: 0;
color: #555;
border-bottom: 1px solid #f4f4f4;
- padding: 5px;
+ padding: 10px;
font-size: 16px;
line-height: 1.1;
}
@@ -1809,214 +2071,201 @@ Component: timeline
padding: 5px;
display: inline-block;
background-color: #fff;
- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.5);
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
border-radius: 4px;
}
.timeline > li > .fa,
.timeline > li > .glyphicon,
.timeline > li > .ion {
- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
width: 30px;
height: 30px;
font-size: 15px;
line-height: 30px;
position: absolute;
color: #666;
- background: #eee;
+ background: #d2d6de;
border-radius: 50%;
text-align: center;
left: 18px;
top: 0;
}
/*
- Component: Buttons
--------------------------
-*/
+ * Component: Button
+ * -----------------
+ */
.btn {
- font-weight: 500;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
border-radius: 3px;
+ -webkit-box-shadow: none;
+ box-shadow: none;
border: 1px solid transparent;
- -webkit-box-shadow: inset 0px -2px 0px 0px rgba(0, 0, 0, 0.09);
- -moz-box-shadow: inset 0px -2px 0px 0px rgba(0, 0, 0, 0.09);
- box-shadow: inset 0px -1px 0px 0px rgba(0, 0, 0, 0.09);
}
-.btn.btn-default {
- background-color: #fafafa;
- color: #666;
- border-color: #ddd;
- border-bottom-color: #ddd;
+.btn.uppercase {
+ text-transform: uppercase;
+}
+.btn.btn-flat {
+ border-radius: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ border-width: 1px;
+}
+.btn:active {
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ -moz-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
+.btn:focus {
+ outline: none;
+}
+.btn.btn-file {
+ position: relative;
+ overflow: hidden;
+}
+.btn.btn-file > input[type='file'] {
+ position: absolute;
+ top: 0;
+ right: 0;
+ min-width: 100%;
+ min-height: 100%;
+ font-size: 100px;
+ text-align: right;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ outline: none;
+ background: white;
+ cursor: inherit;
+ display: block;
}
-.btn.btn-default:hover,
-.btn.btn-default:active,
-.btn.btn-default.hover {
- background-color: #f4f4f4!important;
+.btn-default {
+ background-color: #f4f4f4;
+ color: #444;
+ border-color: #ddd;
}
-.btn.btn-default.btn-flat {
- border-bottom-color: #d9dadc;
+.btn-default:hover,
+.btn-default:active,
+.btn-default.hover {
+ background-color: #e7e7e7 !important;
}
-.btn.btn-primary {
+.btn-primary {
background-color: #3c8dbc;
border-color: #367fa9;
}
-.btn.btn-primary:hover,
-.btn.btn-primary:active,
-.btn.btn-primary.hover {
+.btn-primary:hover,
+.btn-primary:active,
+.btn-primary.hover {
background-color: #367fa9;
}
-.btn.btn-success {
+.btn-success {
background-color: #00a65a;
border-color: #008d4c;
}
-.btn.btn-success:hover,
-.btn.btn-success:active,
-.btn.btn-success.hover {
+.btn-success:hover,
+.btn-success:active,
+.btn-success.hover {
background-color: #008d4c;
}
-.btn.btn-info {
+.btn-info {
background-color: #00c0ef;
border-color: #00acd6;
}
-.btn.btn-info:hover,
-.btn.btn-info:active,
-.btn.btn-info.hover {
+.btn-info:hover,
+.btn-info:active,
+.btn-info.hover {
background-color: #00acd6;
}
-.btn.btn-danger {
- background-color: #f56954;
- border-color: #f4543c;
+.btn-danger {
+ background-color: #dd4b39;
+ border-color: #d73925;
}
-.btn.btn-danger:hover,
-.btn.btn-danger:active,
-.btn.btn-danger.hover {
- background-color: #f4543c;
+.btn-danger:hover,
+.btn-danger:active,
+.btn-danger.hover {
+ background-color: #d73925;
}
-.btn.btn-warning {
+.btn-warning {
background-color: #f39c12;
border-color: #e08e0b;
}
-.btn.btn-warning:hover,
-.btn.btn-warning:active,
-.btn.btn-warning.hover {
+.btn-warning:hover,
+.btn-warning:active,
+.btn-warning.hover {
background-color: #e08e0b;
}
-.btn.btn-flat {
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
- -webkit-box-shadow: none;
- -moz-box-shadow: none;
- box-shadow: none;
- border-width: 1px;
-}
-.btn:active {
- -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
- -moz-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
- box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+.btn-outline {
+ border: 1px solid #fff;
+ background: transparent;
+ color: #fff;
}
-.btn:focus {
- outline: none;
+.btn-outline:hover,
+.btn-outline:focus,
+.btn-outline:active {
+ color: rgba(255, 255, 255, 0.7);
+ border-color: rgba(255, 255, 255, 0.7);
}
-.btn.btn-file {
- position: relative;
- width: 120px;
- height: 35px;
- overflow: hidden;
+.btn-link {
+ -webkit-box-shadow: none;
+ box-shadow: none;
}
-.btn.btn-file > input[type='file'] {
- display: block !important;
- width: 100% !important;
- height: 35px !important;
- opacity: 0 !important;
- position: absolute;
- top: -10px;
- cursor: pointer;
+.btn[class*='bg-']:hover {
+ -webkit-box-shadow: inset 0 0 100px rgba(0, 0, 0, 0.2);
+ box-shadow: inset 0 0 100px rgba(0, 0, 0, 0.2);
}
-.btn.btn-app {
+.btn-app {
+ border-radius: 3px;
position: relative;
padding: 15px 5px;
margin: 0 0 10px 10px;
min-width: 80px;
height: 60px;
- -webkit-box-shadow: none;
- -moz-box-shadow: none;
- box-shadow: none;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
text-align: center;
color: #666;
<TRUNCATED>
[3/4] incubator-kylin git commit: KYLIN-792 ,add performance module
Posted by zh...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/server/src/main/java/org/apache/kylin/rest/service/PerformService.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/kylin/rest/service/PerformService.java b/server/src/main/java/org/apache/kylin/rest/service/PerformService.java
new file mode 100644
index 0000000..35b1207
--- /dev/null
+++ b/server/src/main/java/org/apache/kylin/rest/service/PerformService.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package org.apache.kylin.rest.service;
+
+import au.com.bytecode.opencsv.CSVReader;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.kylin.common.KylinConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.*;
+
+/**
+ * @author jiazhong
+ */
+@Component("performService")
+public class PerformService extends BasicService {
+
+ private static final Logger logger = LoggerFactory.getLogger(PerformService.class);
+
+ /*
+ * @return all query user
+ */
+ public List<String[]> getTotalQueryUser() throws IOException {
+ String filePath = KylinConfig.getInstanceFromEnv().getHdfsWorkingDirectory()+"/performance/metadata/total_query_user.csv";
+ List<String[]> res = readHdfsFile(filePath);
+ logger.info("Total Query User:"+res.get(0)[0]);
+ return res;
+ }
+
+ /*
+ * @return last 30 daily query num
+ */
+ public List<String[]> dailyQueryCount() throws IOException {
+ String filePath = KylinConfig.getInstanceFromEnv().getHdfsWorkingDirectory()+"/performance/metadata/last_30_daily_query_count.csv";
+ List<String[]> res = readHdfsFile(filePath);
+ return res;
+ }
+
+ /*
+ * @return average query count every day
+ */
+ public List<String[]> avgDayQuery() throws IOException {
+ String filePath = KylinConfig.getInstanceFromEnv().getHdfsWorkingDirectory()+"/performance/metadata/avg_day_query.csv";
+ List<String[]> res = readHdfsFile(filePath);
+ logger.info("Avg Day Query:"+res.get(0)[0]);
+ return res;
+ }
+
+ /*
+ *@return average latency every day
+ */
+ public List<String[]> last30DayPercentile() throws IOException {
+ String filePath = KylinConfig.getInstanceFromEnv().getHdfsWorkingDirectory()+"/performance/metadata/last_30_day_90_percentile_latency.csv";
+ List<String[]> res = readHdfsFile(filePath);
+ return res;
+ }
+
+ /*
+ *@return average latency for every cube
+ */
+ public List<String[]> eachDayPercentile() throws IOException {
+ String filePath = KylinConfig.getInstanceFromEnv().getHdfsWorkingDirectory()+"/performance/metadata/each_day_90_95_percentile_latency.csv";
+ List<String[]> res = readHdfsFile(filePath);
+ return res;
+ }
+
+ /*
+ *@return average latency for every cube
+ */
+ public List<String[]> projectPercentile() throws IOException {
+ String filePath = KylinConfig.getInstanceFromEnv().getHdfsWorkingDirectory()+"/performance/metadata/project_90_95_percentile_latency.csv";
+ List<String[]> res = readHdfsFile(filePath);
+ return res;
+ }
+
+
+ private List<String[]> readHdfsFile(String filePath) throws IOException {
+ List<String[]> allRows = null;
+ CSVReader reader = null;
+ FileSystem fs = null;
+ Configuration conf = new Configuration();
+
+ try {
+ fs = FileSystem.newInstance(conf);
+ FSDataInputStream inputStream = fs.open(new Path(filePath));
+ reader = new CSVReader(new InputStreamReader (inputStream),'|');
+
+ //Read all rows at once
+ allRows = reader.readAll();
+
+
+ } catch (IOException e) {
+ logger.info("failed to read hdfs file:",e);
+ }
+ finally {
+ fs.close();
+ }
+ return allRows;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/server/src/main/resources/kylinSecurity.xml
----------------------------------------------------------------------
diff --git a/server/src/main/resources/kylinSecurity.xml b/server/src/main/resources/kylinSecurity.xml
index d372240..22eea4c 100644
--- a/server/src/main/resources/kylinSecurity.xml
+++ b/server/src/main/resources/kylinSecurity.xml
@@ -34,6 +34,7 @@
<scr:intercept-url pattern="/api/cubes/src/tables" access="hasAnyRole('ROLE_ANALYST')" />
<scr:intercept-url pattern="/api/cubes*/**" access="isAuthenticated()" />
<scr:intercept-url pattern="/api/job*/**" access="isAuthenticated()" />
+ <scr:intercept-url pattern="/api/performance*/**" access="isAuthenticated()" />
<scr:intercept-url pattern="/api/admin/config" access="permitAll" />
<scr:intercept-url pattern="/api/projects" access="permitAll" />
<scr:intercept-url pattern="/api/admin*/**" access="hasRole('ROLE_ADMIN')" />
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/a935c7ba/server/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/server/src/main/webapp/WEB-INF/web.xml b/server/src/main/webapp/WEB-INF/web.xml
index d12f81a..703c815 100644
--- a/server/src/main/webapp/WEB-INF/web.xml
+++ b/server/src/main/webapp/WEB-INF/web.xml
@@ -14,10 +14,10 @@ limitations under the License. See accompanying LICENSE file.
-->
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
- version="2.5">
+ version="2.5">
<display-name>Kylin RESt Service</display-name>
@@ -37,10 +37,10 @@ limitations under the License. See accompanying LICENSE file.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
- classpath:applicationContext.xml
- classpath:kylinSecurity.xml
- classpath*:kylin-*-plugin.xml
- </param-value>
+ classpath:applicationContext.xml
+ classpath:kylinSecurity.xml
+ classpath*:kylin-*-plugin.xml
+ </param-value>
</context-param>
<listener>
@@ -50,46 +50,58 @@ limitations under the License. See accompanying LICENSE file.
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
-
+
+ <listener>
+ <listener-class>com.ryantenney.metrics.spring.servlets.MetricsServletsContextListener</listener-class>
+ </listener>
<listener>
- <listener-class>com.ryantenney.metrics.spring.servlets.MetricsServletsContextListener</listener-class>
+ <listener-class>org.apache.kylin.rest.metrics.KylinInstrumentedFilterContextListener</listener-class>
</listener>
+
<listener>
- <listener-class>org.apache.kylin.rest.metrics.KylinInstrumentedFilterContextListener</listener-class>
+ <listener-class>
+ org.springframework.security.web.session.HttpSessionEventPublisher
+ </listener-class>
</listener>
- <listener>
- <listener-class>
- org.springframework.security.web.session.HttpSessionEventPublisher
- </listener-class>
- </listener>
-
-<filter>
- <filter-name>CORS</filter-name>
- <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
- <init-param>
- <param-name>cors.supportedHeaders</param-name>
- <param-value>Authorization,Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, Accept</param-value>
- </init-param>
- <init-param>
- <param-name>cors.supportedMethods</param-name>
- <param-value>GET, POST, PUT, DELETE, OPTIONS</param-value>
- </init-param>
- <init-param>
- <param-name>cors.supportsCredentials </param-name>
- <param-value>true</param-value>
- </init-param>
-</filter>
-
-<filter-mapping>
- <filter-name>CORS</filter-name>
- <url-pattern>/*</url-pattern>
-</filter-mapping>
-
- <!--
- Apply Spring Security Filter to all Requests
- -->
- <filter>
+ <filter>
+ <filter-name>CORS</filter-name>
+ <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
+ <init-param>
+ <param-name>cors.supportedHeaders</param-name>
+ <param-value>Authorization,Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified,
+ Cache-Control, Expires, Content-Type, X-E4M-With, Accept
+ </param-value>
+ </init-param>
+ <init-param>
+ <param-name>cors.supportedMethods</param-name>
+ <param-value>GET, POST, PUT, DELETE, OPTIONS</param-value>
+ </init-param>
+ <init-param>
+ <param-name>cors.supportsCredentials</param-name>
+ <param-value>true</param-value>
+ </init-param>
+ </filter>
+
+ <filter-mapping>
+ <filter-name>CORS</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+ <filter>
+ <filter-name>loggingFilter</filter-name>
+ <filter-class>org.apache.kylin.rest.filter.KylinApiFilter</filter-class>
+ </filter>
+
+ <filter-mapping>
+ <filter-name>loggingFilter</filter-name>
+ <url-pattern>/api/*</url-pattern>
+ </filter-mapping>
+
+ <!--
+ Apply Spring Security Filter to all Requests
+ -->
+ <filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
@@ -98,15 +110,15 @@ limitations under the License. See accompanying LICENSE file.
<url-pattern>/*</url-pattern>
</filter-mapping>
- <filter>
- <filter-name>instrumentedFilter</filter-name>
- <filter-class>com.codahale.metrics.servlet.InstrumentedFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>instrumentedFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
-
+ <filter>
+ <filter-name>instrumentedFilter</filter-name>
+ <filter-class>com.codahale.metrics.servlet.InstrumentedFilter</filter-class>
+ </filter>
+ <filter-mapping>
+ <filter-name>instrumentedFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
<servlet>
<servlet-name>kylin</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
@@ -125,7 +137,7 @@ limitations under the License. See accompanying LICENSE file.
<servlet-name>metricsservlet</servlet-name>
<url-pattern>/metrics/*</url-pattern>
</servlet-mapping>
-
+
<servlet>
<servlet-name>metricsadminservlet</servlet-name>
<servlet-class>com.codahale.metrics.servlets.AdminServlet</servlet-class>
@@ -135,9 +147,9 @@ limitations under the License. See accompanying LICENSE file.
<servlet-name>metricsadminservlet</servlet-name>
<url-pattern>/metricsadmin/*</url-pattern>
</servlet-mapping>
-
- <!--
- <servlet>
+
+ <!--
+ <servlet>
<servlet-name>matricsadmin</servlet-name>
<servlet-class>com.codahale.metrics.servlets.MetricsServlet</servlet-class>
<load-on-startup>1</load-on-startup>