diff --git a/config/tripleo_ui_config.js.sample b/config/tripleo_ui_config.js.sample index 4ada3a07..2b7d18bf 100644 --- a/config/tripleo_ui_config.js.sample +++ b/config/tripleo_ui_config.js.sample @@ -24,5 +24,6 @@ window.tripleOUiConfig = { // 'excludedLanguages': ['de', 'ja'], // Logging - // 'loggers': ['console'] + // 'loggers': ['console', 'zaqar'] + // 'logger-zaqar-queue': 'tripleo-ui-logging' }; diff --git a/releasenotes/notes/zaqar-adapter-948a636d67781cd4.yaml b/releasenotes/notes/zaqar-adapter-948a636d67781cd4.yaml new file mode 100644 index 00000000..69a0f3c8 --- /dev/null +++ b/releasenotes/notes/zaqar-adapter-948a636d67781cd4.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Implements `websocket-logging `__ + Implement Zaqar logger adapter diff --git a/src/__tests__/components/plans/PlansList.tests.js b/src/__tests__/components/plans/PlansList.tests.js index 56cbd08a..d30b50bb 100644 --- a/src/__tests__/components/plans/PlansList.tests.js +++ b/src/__tests__/components/plans/PlansList.tests.js @@ -19,10 +19,10 @@ import { Map } from 'immutable'; import React from 'react'; import ReactShallowRenderer from 'react-test-renderer/shallow'; +import store from '../../../js/store'; import PlansList from '../../../js/components/plan/PlansList'; import FileList from '../../../js/components/plan/FileList'; import { PlanFile } from '../../../js/immutableRecords/plans'; -import store from '../../../js/store'; describe('PlansList component', () => { let output; diff --git a/src/__tests__/reducers/loggerReducer.tests.js b/src/__tests__/reducers/loggerReducer.tests.js new file mode 100644 index 00000000..b2d18ce5 --- /dev/null +++ b/src/__tests__/reducers/loggerReducer.tests.js @@ -0,0 +1,63 @@ +/** + * Copyright 2017 Red Hat Inc. + * + * Licensed 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. + */ + +import { List, Map } from 'immutable'; + +import { InitialLoggerState } from '../../js/immutableRecords/logger'; +import LoggerActions from '../../js/actions/LoggerActions'; +import loggerReducer from '../../js/reducers/loggerReducer'; + +describe('loggerReducer state', () => { + describe('default state', () => { + let state; + + beforeEach(() => { + state = loggerReducer(undefined, { type: 'undefined-action' }); + }); + + it('`authenticated` is false', () => { + expect(state.get('authenticated')).toBe(false); + }); + + it('`messages` is empty', () => { + expect(state.get('messages').isEmpty()).toBe(true); + }); + }); + + describe('QUEUE_MESSAGE', () => { + it('enqueues a messages', () => { + let state = loggerReducer( + new InitialLoggerState(), + LoggerActions.queueMessage(1) + ); + expect(state.get('messages').size).toEqual(1); + }); + }); + + describe('FLUSH_MESSAGES_SUCCESS', () => { + it('flushes messages', () => { + let state = loggerReducer( + Map({ + messages: List([1, 2, 3]), + authenticated: true + }), + LoggerActions.flushMessagesSuccess() + ); + + expect(state.get('messages').isEmpty()).toBe(true); + }); + }); +}); diff --git a/src/js/actions/EnvironmentConfigurationActions.js b/src/js/actions/EnvironmentConfigurationActions.js index 9f624534..63e24f96 100644 --- a/src/js/actions/EnvironmentConfigurationActions.js +++ b/src/js/actions/EnvironmentConfigurationActions.js @@ -25,7 +25,7 @@ import NotificationActions from '../actions/NotificationActions'; import MistralApiErrorHandler from '../services/MistralApiErrorHandler'; import { topicSchema } from '../normalizrSchemas/environmentConfiguration'; import MistralConstants from '../constants/MistralConstants'; -import logger from '../services/logger'; +import logger from '../services/logging/LoggingService'; import SwiftApiErrorHandler from '../services/SwiftApiErrorHandler'; import SwiftApiService from '../services/SwiftApiService'; diff --git a/src/js/actions/LoggerActions.js b/src/js/actions/LoggerActions.js new file mode 100644 index 00000000..e7305664 --- /dev/null +++ b/src/js/actions/LoggerActions.js @@ -0,0 +1,67 @@ +/** + * Copyright 2017 Red Hat Inc. + * + * Licensed 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. + */ + +import when from 'when'; +import LoggerConstants from '../constants/LoggerConstants'; +import ZaqarWebSocketService from '../services/ZaqarWebSocketService'; +import NotificationActions from '../actions/NotificationActions'; + +export default { + queueMessage(message) { + return { + type: LoggerConstants.QUEUE_MESSAGE, + payload: message + }; + }, + + flushMessagesSuccess() { + return { + type: LoggerConstants.FLUSH_MESSAGES_SUCCESS + }; + }, + + flushMessages() { + return (dispatch, getState) => { + const messages = getState().logger.messages; + + when + .all(() => { + messages.map(message => { + ZaqarWebSocketService.sendMessage('message_post', message); + }); + }) + .then(() => { + dispatch(this.flushMessagesSuccess()); + }) + .catch(error => { + // We're using `console` here to avoid circular imports. + console.error(error); // eslint-disable-line no-console + dispatch( + NotificationActions.notify({ + title: 'Logging error', + message: 'Failed to flush Zaqar messages.' + }) + ); + }); + }; + }, + + authenticated() { + return { + type: LoggerConstants.WS_AUTHENTICATION_SUCCESS + }; + } +}; diff --git a/src/js/actions/LoginActions.js b/src/js/actions/LoginActions.js index 75ba6ba3..35284aa1 100644 --- a/src/js/actions/LoginActions.js +++ b/src/js/actions/LoginActions.js @@ -20,7 +20,7 @@ import KeystoneApiErrorHandler from '../services/KeystoneApiErrorHandler'; import KeystoneApiService from '../services/KeystoneApiService'; import LoginConstants from '../constants/LoginConstants'; import ZaqarWebSocketService from '../services/ZaqarWebSocketService'; -import logger from '../services/logger'; +import logger from '../services/logging/LoggingService'; import cookie from 'react-cookie'; export default { diff --git a/src/js/actions/NodesActions.js b/src/js/actions/NodesActions.js index c9cc85f9..3eedd800 100644 --- a/src/js/actions/NodesActions.js +++ b/src/js/actions/NodesActions.js @@ -34,7 +34,7 @@ import { introspectionStatusSchema } from '../normalizrSchemas/nodes'; import MistralConstants from '../constants/MistralConstants'; -import logger from '../services/logger'; +import logger from '../services/logging/LoggingService'; import { setNodeCapability } from '../utils/nodes'; const messages = defineMessages({ diff --git a/src/js/actions/ParametersActions.js b/src/js/actions/ParametersActions.js index 0ad685ca..5f96c3f5 100644 --- a/src/js/actions/ParametersActions.js +++ b/src/js/actions/ParametersActions.js @@ -22,7 +22,7 @@ import ParametersConstants from '../constants/ParametersConstants'; import MistralApiService from '../services/MistralApiService'; import MistralApiErrorHandler from '../services/MistralApiErrorHandler'; import MistralConstants from '../constants/MistralConstants'; -import logger from '../services/logger'; +import logger from '../services/logging/LoggingService'; const messages = defineMessages({ parametersUpdatedNotficationTitle: { diff --git a/src/js/actions/PlansActions.js b/src/js/actions/PlansActions.js index 82c773a5..eecbdc18 100644 --- a/src/js/actions/PlansActions.js +++ b/src/js/actions/PlansActions.js @@ -19,7 +19,7 @@ import { fromJS } from 'immutable'; import { normalize, arrayOf } from 'normalizr'; import when from 'when'; -import logger from '../services/logger'; +import logger from '../services/logging/LoggingService'; import MistralApiService from '../services/MistralApiService'; import MistralApiErrorHandler from '../services/MistralApiErrorHandler'; import NotificationActions from '../actions/NotificationActions'; diff --git a/src/js/actions/RegisterNodesActions.js b/src/js/actions/RegisterNodesActions.js index 2e457c85..17bd2dbd 100644 --- a/src/js/actions/RegisterNodesActions.js +++ b/src/js/actions/RegisterNodesActions.js @@ -27,7 +27,7 @@ import NodesActions from './NodesActions'; import { nodeSchema } from '../normalizrSchemas/nodes'; import ValidationsActions from './ValidationsActions'; import MistralConstants from '../constants/MistralConstants'; -import logger from '../services/logger'; +import logger from '../services/logging/LoggingService'; const messages = defineMessages({ registrationNotificationTitle: { diff --git a/src/js/actions/RolesActions.js b/src/js/actions/RolesActions.js index c7ec7aaf..ff28b5d7 100644 --- a/src/js/actions/RolesActions.js +++ b/src/js/actions/RolesActions.js @@ -19,7 +19,7 @@ import RolesConstants from '../constants/RolesConstants'; import MistralApiService from '../services/MistralApiService'; import MistralApiErrorHandler from '../services/MistralApiErrorHandler'; import MistralConstants from '../constants/MistralConstants'; -import logger from '../services/logger'; +import logger from '../services/logging/LoggingService'; export default { fetchRoles(planName) { diff --git a/src/js/actions/StacksActions.js b/src/js/actions/StacksActions.js index 98cc55a7..1a729573 100644 --- a/src/js/actions/StacksActions.js +++ b/src/js/actions/StacksActions.js @@ -21,7 +21,7 @@ import HeatApiService from '../services/HeatApiService'; import NotificationActions from '../actions/NotificationActions'; import StacksConstants from '../constants/StacksConstants'; import { stackSchema, stackResourceSchema } from '../normalizrSchemas/stacks'; -import logger from '../services/logger'; +import logger from '../services/logging/LoggingService'; export default { fetchStacksPending() { diff --git a/src/js/actions/ValidationsActions.js b/src/js/actions/ValidationsActions.js index af176bd0..2dfec022 100644 --- a/src/js/actions/ValidationsActions.js +++ b/src/js/actions/ValidationsActions.js @@ -24,7 +24,7 @@ import MistralApiErrorHandler from '../services/MistralApiErrorHandler'; import ValidationsConstants from '../constants/ValidationsConstants'; import { validationSchema } from '../normalizrSchemas/validations'; import MistralConstants from '../constants/MistralConstants'; -import logger from '../services/logger'; +import logger from '../services/logging/LoggingService'; export default { fetchValidations() { diff --git a/src/js/actions/WorkflowExecutionsActions.js b/src/js/actions/WorkflowExecutionsActions.js index 29a34aac..e74d67b9 100644 --- a/src/js/actions/WorkflowExecutionsActions.js +++ b/src/js/actions/WorkflowExecutionsActions.js @@ -24,7 +24,7 @@ import WorkflowExecutionsConstants import { workflowExecutionSchema } from '../normalizrSchemas/workflowExecutions'; -import logger from '../services/logger'; +import logger from '../services/logging/LoggingService'; export default { fetchWorkflowExecutions() { diff --git a/src/js/actions/ZaqarActions.js b/src/js/actions/ZaqarActions.js index 89a0cbb4..c1bf24ff 100644 --- a/src/js/actions/ZaqarActions.js +++ b/src/js/actions/ZaqarActions.js @@ -14,6 +14,8 @@ * under the License. */ +import { get } from 'lodash'; +import LoggerActions from './LoggerActions'; import NodesActions from './NodesActions'; import PlansActions from './PlansActions'; import RegisterNodesActions from './RegisterNodesActions'; @@ -28,8 +30,18 @@ export default { }; }, + handleAuthenticationSuccess(message, dispatch) { + message = get(message, ['body', 'message']); + + if (message === 'Authentified.') { + dispatch(LoggerActions.authenticated()); + dispatch(LoggerActions.flushMessages()); + } + }, + messageReceived(message, history) { return (dispatch, getState) => { + this.handleAuthenticationSuccess(message, dispatch); const { type, payload } = message.body; switch (type) { case MistralConstants.BAREMETAL_REGISTER_OR_UPDATE: @@ -74,5 +86,31 @@ export default { break; } }; + }, + + postMessage(queueName, body, ttl = 3600) { + return (dispatch, getState) => { + const message = { + queue_name: queueName, + messages: [ + { + body, + ttl + } + ] + }; + + // Drop the message on the floor when there is no `store` + if (!getState) { + return; + } + + if (!getState().logger.authenticated) { + dispatch(LoggerActions.queueMessage(message)); + return; + } + + ZaqarWebSocketService.sendMessage('message_post', message); + }; } }; diff --git a/src/js/components/AuthenticatedContent.js b/src/js/components/AuthenticatedContent.js index bc056169..dd4db50e 100644 --- a/src/js/components/AuthenticatedContent.js +++ b/src/js/components/AuthenticatedContent.js @@ -42,9 +42,9 @@ const messages = defineMessages({ class AuthenticatedContent extends React.Component { componentDidMount() { + this.props.initializeZaqarConnection(); this.props.fetchPlans(); this.props.fetchWorkflowExecutions(); - this.props.initializeZaqarConnection(); } render() { diff --git a/src/js/constants/LoggerConstants.js b/src/js/constants/LoggerConstants.js new file mode 100644 index 00000000..f17f2d7a --- /dev/null +++ b/src/js/constants/LoggerConstants.js @@ -0,0 +1,23 @@ +/** + * Copyright 2017 Red Hat Inc. + * + * Licensed 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. + */ + +import keyMirror from 'keymirror'; + +export default keyMirror({ + QUEUE_MESSAGE: null, + FLUSH_MESSAGES_SUCCESS: null, + WS_AUTHENTICATION_SUCCESS: null +}); diff --git a/src/js/constants/ZaqarConstants.js b/src/js/constants/ZaqarConstants.js index 1f314138..fc468c43 100644 --- a/src/js/constants/ZaqarConstants.js +++ b/src/js/constants/ZaqarConstants.js @@ -14,8 +14,11 @@ * under the License. */ -let appConfig = window.tripleOUiConfig || {}; +import { getAppConfig } from '../services/utils'; -let zaqarDefaultQueue = appConfig.zaqar_default_queue || 'tripleo'; +let zaqarDefaultQueue = getAppConfig()['zaqar_default_queue'] || 'tripleo'; +let zaqarLoggingQueue = + getAppConfig()['logger-zaqar-queue'] || 'tripleo-ui-logging'; export const ZAQAR_DEFAULT_QUEUE = zaqarDefaultQueue; +export const ZAQAR_LOGGING_QUEUE = zaqarLoggingQueue; diff --git a/src/js/immutableRecords/logger.js b/src/js/immutableRecords/logger.js new file mode 100644 index 00000000..06556927 --- /dev/null +++ b/src/js/immutableRecords/logger.js @@ -0,0 +1,22 @@ +/** + * Copyright 2017 Red Hat Inc. + * + * Licensed 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. + */ + +import { List, Record } from 'immutable'; + +export const InitialLoggerState = Record({ + messages: List(), + authenticated: false +}); diff --git a/src/js/reducers/appReducer.js b/src/js/reducers/appReducer.js index cd8be93f..ed344b99 100644 --- a/src/js/reducers/appReducer.js +++ b/src/js/reducers/appReducer.js @@ -20,6 +20,7 @@ import { reducer as formReducer } from 'redux-form'; import environmentConfigurationReducer from './environmentConfigurationReducer'; import filtersReducer from './filtersReducer'; import i18nReducer from './i18nReducer'; +import loggerReducer from './loggerReducer'; import loginReducer from './loginReducer'; import nodesReducer from './nodesReducer'; import notificationsReducer from './notificationsReducer'; @@ -36,6 +37,7 @@ const appReducer = combineReducers({ executions: workflowExecutionsReducer, filters: filtersReducer, i18n: i18nReducer, + logger: loggerReducer, login: loginReducer, nodes: nodesReducer, notifications: notificationsReducer, diff --git a/src/js/reducers/loggerReducer.js b/src/js/reducers/loggerReducer.js new file mode 100644 index 00000000..dd26ad2b --- /dev/null +++ b/src/js/reducers/loggerReducer.js @@ -0,0 +1,39 @@ +/** + * Copyright 2017 Red Hat Inc. + * + * Licensed 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. + */ + +import { List } from 'immutable'; +import { InitialLoggerState } from '../immutableRecords/logger'; +import LoggerConstants from '../constants/LoggerConstants'; + +const initialState = new InitialLoggerState(); + +export default function loggerReduder(state = initialState, action) { + switch (action.type) { + case LoggerConstants.QUEUE_MESSAGE: + return state.update('messages', messages => + messages.push(action.payload) + ); + + case LoggerConstants.WS_AUTHENTICATION_SUCCESS: + return state.set('authenticated', true); + + case LoggerConstants.FLUSH_MESSAGES_SUCCESS: + return state.set('messages', List()); + + default: + return state; + } +} diff --git a/src/js/services/SwiftApiService.js b/src/js/services/SwiftApiService.js index ce40c5a0..dc646a66 100644 --- a/src/js/services/SwiftApiService.js +++ b/src/js/services/SwiftApiService.js @@ -19,7 +19,7 @@ import request from 'reqwest'; import when from 'when'; import { getAuthTokenId, getServiceUrl } from '../services/utils'; -import logger from '../services/logger'; +import logger from '../services/logging/LoggingService'; class SwiftApiService { defaultRequest(path, additionalAttributes) { diff --git a/src/js/services/ZaqarWebSocketService.js b/src/js/services/ZaqarWebSocketService.js index acdfc66a..dabbf108 100644 --- a/src/js/services/ZaqarWebSocketService.js +++ b/src/js/services/ZaqarWebSocketService.js @@ -21,7 +21,13 @@ import { getAuthTokenId, getProjectId, getServiceUrl } from './utils'; import { ZAQAR_DEFAULT_QUEUE } from '../constants/ZaqarConstants'; import ZaqarActions from '../actions/ZaqarActions'; import NotificationActions from '../actions/NotificationActions'; -import logger from '../services/logger'; + +// We're using `console` here to avoid circular imports. +const logger = { + error: (...msg) => { + console.log(...msg); // eslint-disable-line no-console + } +}; export default { socket: null, @@ -55,7 +61,8 @@ export default { }; this.socket.onmessage = evt => { - dispatch(ZaqarActions.messageReceived(JSON.parse(evt.data), history)); + const data = JSON.parse(evt.data); + dispatch(ZaqarActions.messageReceived(data, history)); }; }); }, diff --git a/src/js/services/logger.js b/src/js/services/logging/LoggingService.js similarity index 62% rename from src/js/services/logger.js rename to src/js/services/logging/LoggingService.js index 6c8d8238..37d79566 100644 --- a/src/js/services/logger.js +++ b/src/js/services/logging/LoggingService.js @@ -25,66 +25,17 @@ // // Usage: // -// import logger from 'src/js/services/logger'; +// import logger from 'src/js/services/logging/LoggingService'; // logger.log('Hello world!'); -class Adapter { - debug(...args) {} - - info(...args) {} - - warn(...args) {} - - error(...args) {} - - group(...args) {} - - groupCollapsed(...args) {} - - groupEnd(...args) {} - - log(...args) {} -} - -class ConsoleAdapter extends Adapter { - debug(...args) { - console.debug(...args); // eslint-disable-line no-console - } - - info(...args) { - console.info(...args); // eslint-disable-line no-console - } - - warn(...args) { - console.warn(...args); // eslint-disable-line no-console - } - - error(...args) { - console.error(...args); // eslint-disable-line no-console - } - - group(...args) { - console.group(...args); // eslint-disable-line no-console - } - - groupCollapsed(...args) { - console.groupCollapsed(...args); // eslint-disable-line no-console - } - - groupEnd(...args) { - console.groupEnd(...args); // eslint-disable-line no-console - } - - log(...args) { - console.log(...args); // eslint-disable-line no-console - } -} - -class ZaqarAdapter extends Adapter {} +import { includes } from 'lodash'; +import LoggerConstants from '../../constants/LoggerConstants'; +import ConsoleAdapter from './adapters/ConsoleAdapter'; +import ZaqarAdapter from './adapters/ZaqarAdapter'; const AVAILABLE_ADAPTERS = { - console: new ConsoleAdapter(), - zaqar: new ZaqarAdapter() + console: ConsoleAdapter, + zaqar: ZaqarAdapter }; class Logger { @@ -101,10 +52,7 @@ class Logger { constructor() { this.adapters = []; - - if (window.tripleOUiConfig !== undefined) { - this.loadAdapters(); - } + this.reduxDispatch = null; this.AVAILABLE_FUNCTIONS.forEach(fn => { this[fn] = function(...args) { @@ -115,19 +63,20 @@ class Logger { this.registerGlobalErrorHandler(); } + setReduxDispatch(dispatchFunction) { + this.reduxDispatch = dispatchFunction; + this.loadAdapters(); + } + loadAdapters() { if (this.adapters.length) { return; } - if (window.tripleOUiConfig === undefined) { - return; - } - - let enabledAdapters = window.tripleOUiConfig.loggers || ['console']; + let enabledAdapters = (window.tripleOUiConfig || {}).loggers || ['console']; enabledAdapters.forEach(adapter => { - let instance = AVAILABLE_ADAPTERS[adapter]; + let instance = new AVAILABLE_ADAPTERS[adapter](this.reduxDispatch); if (instance === undefined) { throw Error(`Adapter ${adapter} not defined`); @@ -138,8 +87,6 @@ class Logger { } dispatch(fn, ...args) { - this.loadAdapters(); - this.adapters.forEach(adapter => { let f = adapter[fn]; @@ -159,4 +106,11 @@ class Logger { } } +const isLoggerAction = action => includes(LoggerConstants, action.type); + +// The `predicate` prevents redux-logger from logging logger messages +// in order to avoid an infinite loop. This function is used in `createLogger` +// in store.js. +export const predicate = (getState, action) => !isLoggerAction(action); + export default new Logger(); diff --git a/src/js/services/logging/adapters/BaseAdapter.js b/src/js/services/logging/adapters/BaseAdapter.js new file mode 100644 index 00000000..a7fe575b --- /dev/null +++ b/src/js/services/logging/adapters/BaseAdapter.js @@ -0,0 +1,21 @@ +export default class Adapter { + constructor(dispatch) { + this._dispatch = dispatch; + } + + debug(...args) {} + + info(...args) {} + + warn(...args) {} + + error(...args) {} + + group(...args) {} + + groupCollapsed(...args) {} + + groupEnd(...args) {} + + log(...args) {} +} diff --git a/src/js/services/logging/adapters/ConsoleAdapter.js b/src/js/services/logging/adapters/ConsoleAdapter.js new file mode 100644 index 00000000..45492409 --- /dev/null +++ b/src/js/services/logging/adapters/ConsoleAdapter.js @@ -0,0 +1,35 @@ +import Adapter from './BaseAdapter'; + +export default class ConsoleAdapter extends Adapter { + debug(...args) { + console.debug(...args); // eslint-disable-line no-console + } + + info(...args) { + console.info(...args); // eslint-disable-line no-console + } + + warn(...args) { + console.warn(...args); // eslint-disable-line no-console + } + + error(...args) { + console.error(...args); // eslint-disable-line no-console + } + + group(...args) { + console.group(...args); // eslint-disable-line no-console + } + + groupCollapsed(...args) { + console.groupCollapsed(...args); // eslint-disable-line no-console + } + + groupEnd(...args) { + console.groupEnd(...args); // eslint-disable-line no-console + } + + log(...args) { + console.log(...args); // eslint-disable-line no-console + } +} diff --git a/src/js/services/logging/adapters/ZaqarAdapter.js b/src/js/services/logging/adapters/ZaqarAdapter.js new file mode 100644 index 00000000..d45d53f3 --- /dev/null +++ b/src/js/services/logging/adapters/ZaqarAdapter.js @@ -0,0 +1,70 @@ +import { ZAQAR_LOGGING_QUEUE } from '../../../constants/ZaqarConstants'; +import ZaqarActions from '../../../actions/ZaqarActions'; +import Adapter from './BaseAdapter'; + +export default class ZaqarAdapter extends Adapter { + constructor(dispatch) { + super(dispatch); + this.indent = 0; + this.buffer = []; + } + + _formatMessage(message, level) { + return { + message, + level, + timestamp: Date.now() + }; + } + + _send(message, level) { + if (this.indent !== 0) { + this.buffer.push({ + message, + level + }); + return; + } + + const msg = this._formatMessage(message[0], level); + this._dispatch(ZaqarActions.postMessage(ZAQAR_LOGGING_QUEUE, msg)); + } + + debug(...args) { + this._send(args, 'debug'); + } + + info(...args) { + this._send(args, 'info'); + } + + warn(...args) { + this._send(args, 'warn'); + } + + error(...args) { + this._send(args, 'error'); + } + + group(...args) { + this.indent++; + } + + groupCollapsed(...args) { + this.indent++; + } + + groupEnd(...args) { + this.indent--; + + this.buffer.map(m => { + this._send(m.message, m.level); + }); + + this.buffer = []; + } + + log(...args) { + this.info(args); + } +} diff --git a/src/js/store.js b/src/js/store.js index d70e3579..7fa3d8f4 100644 --- a/src/js/store.js +++ b/src/js/store.js @@ -18,7 +18,7 @@ import { applyMiddleware, createStore } from 'redux'; import cookie from 'react-cookie'; import thunkMiddleware from 'redux-thunk'; import createLogger from 'redux-logger'; -import logger from './services/logger'; +import logger, { predicate } from './services/logging/LoggingService'; import appReducer from './reducers/appReducer'; import { InitialPlanState } from './immutableRecords/plans'; @@ -44,7 +44,17 @@ function getStoredPlanName() { const loggerMiddleware = createLogger({ collapsed: true, - logger: logger + predicate: predicate, + logger: logger, + // We're turning off all colors here because the formatting chars obscure the + // content server-side. + colors: { + title: false, + prevState: false, + action: false, + nextState: false, + error: false + } }); const store = createStore( @@ -56,4 +66,6 @@ const store = createStore( ) ); +logger.setReduxDispatch(store.dispatch); + export default store;