log_tail_view.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var App = require('app');
  19. var dateUtils = require('utils/date/date');
  20. App.LogTailView = Em.View.extend(App.InfiniteScrollMixin, {
  21. startIndex: 0,
  22. selectedTailCount: 50,
  23. autoResize: false,
  24. logRows: Em.A([]),
  25. totalCount: 0,
  26. totalItems: 0,
  27. lastLogTime: 0,
  28. isDataReady: false,
  29. refreshEnd: true,
  30. pollLogs: true,
  31. pollLogInterval: 2000,
  32. pollLogTimeoutId: null,
  33. oldLogsAvailable: false,
  34. oldLogsIsFetching: false,
  35. templateName: require('templates/common/log_tail'),
  36. content: null,
  37. isVisible: Em.computed.not('App.isClusterUser'),
  38. /**
  39. * elements size are:
  40. * .modal margin 40px x 2
  41. * .modal inner padding 15px x 2
  42. * .top-wrap-header height + margin 60px
  43. * .modal-footer 60px
  44. * .log-tail-popup-info 45px
  45. *
  46. * @property {number} resizeDelta
  47. */
  48. resizeDelta: 15*2 + 60 + 60 + 40 + 45,
  49. didInsertElement: function() {
  50. var self = this;
  51. this.infiniteScrollInit(this.$('.log-tail-content'), {
  52. appendHtml: '<span id="empty-span"></span>'
  53. });
  54. this.fetchRows({
  55. startIndex: 0
  56. }).then(function(data) {
  57. var logs = self.fetchRowsSuccess(data);
  58. self.infiniteScrollSetDataAvailable(true);
  59. self.appendLogRows(logs.reverse());
  60. self.saveLastTimestamp(logs);
  61. self.set('isDataReady', true);
  62. self.scrollToBottom();
  63. self.startLogPolling();
  64. });
  65. this.subscribeResize();
  66. },
  67. scrollToBottom: function() {
  68. Em.run.next(this, function() {
  69. this.$('.log-tail-content').scrollTop(this.$('.log-tail-content').prop('scrollHeight'));
  70. });
  71. },
  72. _infiniteScrollHandler: function(e) {
  73. var self = this;
  74. this._super(e);
  75. if ($(e.target).scrollTop() === 0) {
  76. if (this.get('noOldLogs') && !this.get('oldLogsIsFetching')) return;
  77. self.set('oldLogsIsFetching', true);
  78. this.fetchRows(this.oldestLogs()).then(function(data) {
  79. var oldestLog = self.get('logRows.0.logtime');
  80. var logRows = self.fetchRowsSuccess(data).filter(function(i) {
  81. return parseInt(i.get('logtime'), 10) < parseInt(oldestLog, 10);
  82. });
  83. if (logRows.length) {
  84. self.get('logRows').unshiftObjects(logRows.reverse());
  85. } else {
  86. self.set('noOldLogs', true);
  87. }
  88. self.set('oldLogsIsFetching', false);
  89. });
  90. }
  91. },
  92. willDestroyElement: function() {
  93. this._super();
  94. this.stopLogPolling();
  95. this.unsubscribeResize();
  96. this.get('logRows').clear();
  97. },
  98. resizeHandler: function() {
  99. // window.innerHeight * 0.1 = modal popup top 5% from both sides = 10%
  100. if (this.get('state') === 'destroyed') return;
  101. var newSize = $(window).height() - this.get('resizeDelta') - window.innerHeight*0.08;
  102. this.$().find('.log-tail-content.pre-styled').css('maxHeight', newSize + 'px');
  103. },
  104. unsubscribeResize: function() {
  105. if (!this.get('autoResize')) return;
  106. $(window).off('resize', this.resizeHandler.bind(this));
  107. },
  108. subscribeResize: function() {
  109. if (!this.get('autoResize')) return;
  110. this.resizeHandler();
  111. $(window).on('resize', this.resizeHandler.bind(this));
  112. },
  113. fetchRows: function(opts) {
  114. var options = $.extend({}, true, {
  115. startIndex: this.get('startIndex'),
  116. pageSize: this.get('selectedTailCount')
  117. }, opts || {});
  118. return App.ajax.send({
  119. sender: this,
  120. name: 'logtail.get',
  121. data: {
  122. hostName: this.get('content.hostName'),
  123. logComponentName: this.get('content.logComponentName'),
  124. pageSize: "" + options.pageSize,
  125. startIndex: "" + options.startIndex
  126. },
  127. error: 'fetchRowsError'
  128. });
  129. },
  130. fetchRowsSuccess: function(data) {
  131. var logRows = Em.getWithDefault(data, 'logList', []).map(function(logItem, i) {
  132. var item = App.keysUnderscoreToCamelCase(App.permit(logItem, ['log_message', 'logtime', 'level', 'id']));
  133. item.logtimeFormatted = dateUtils.dateFormat(parseInt(item.logtime, 10), 'YYYY-MM-DD HH:mm:ss,SSS');
  134. return Em.Object.create(item);
  135. });
  136. if (logRows.length === 0) {
  137. this.infiniteScrollSetDataAvailable(false);
  138. return [];
  139. }
  140. return logRows;
  141. },
  142. fetchRowsError: function() {},
  143. /** actions **/
  144. openInLogSearch: function() {},
  145. refreshContent: function() {
  146. var self = this,
  147. allIds;
  148. if (!this.get('refreshEnd')) {
  149. return $.Deferred().resolve().promise();
  150. }
  151. this.set('refreshEnd', false);
  152. //this.infiniteScrollSetDataAvailable(true);
  153. allIds = this.get('logRows').mapProperty('id');
  154. return this.fetchRows(this.currentPage()).then(function(data) {
  155. var logRows = self.fetchRowsSuccess(data).filter(function(i) {
  156. return parseInt(i.logtime, 10) > parseInt(self.get('lastLogTime'), 10) && !allIds.contains(i.get('id'));
  157. });
  158. if (logRows.length) {
  159. self.appendLogRows(logRows.reverse());
  160. self.saveLastTimestamp(logRows);
  161. }
  162. self.set('refreshEnd', true);
  163. });
  164. },
  165. appendLogRows: function(logRows) {
  166. this.get('logRows').pushObjects(logRows);
  167. },
  168. saveLastTimestamp: function(logRows) {
  169. this.set('lastLogTime', Em.getWithDefault(logRows, '0.logtime', 0));
  170. return this.get('lastLogTime');
  171. },
  172. currentPage: function() {
  173. return {
  174. startIndex: 0,
  175. pageSize: this.get('selectedTailCount')
  176. };
  177. },
  178. nextPage: function() {
  179. var newIndex = this.get('startIndex') + this.get('selectedTailCount');
  180. if (newIndex < 0) {
  181. newIndex = 0;
  182. }
  183. this.set('startIndex', newIndex);
  184. return {
  185. startIndex: newIndex,
  186. pageSize: this.get('selectedTailCount')
  187. };
  188. },
  189. oldestLogs: function() {
  190. return {
  191. startIndex: this.get('logRows.length'),
  192. pageSize: this.get('selectedTailCount')
  193. };
  194. },
  195. startLogPolling: function() {
  196. var self = this;
  197. if (!this.get('pollLogs') || this.get('state') === 'destroyed') return;
  198. this.set('pollLogTimeoutId', setTimeout(function() {
  199. self.stopLogPolling();
  200. self.refreshContent().then(self.startLogPolling.bind(self));
  201. }, this.get('pollLogInterval')));
  202. },
  203. stopLogPolling: function() {
  204. if (!this.get('pollLogs') || this.get('pollLogTimeoutId') === null) return;
  205. clearTimeout(this.get('pollLogTimeoutId'));
  206. }
  207. });