3 * Copyright 2016 RIFT.IO Inc
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 import React from 'react';
19 import _isEmpty from 'lodash/isEmpty';
20 import _find from 'lodash/find';
21 import _cloneDeep from 'lodash/cloneDeep';
22 import './logging.scss';
24 import Button from 'widgets/button/rw.button.js';
25 import DashboardCard from 'widgets/dashboard_card/dashboard_card.jsx';
26 import ScreenLoader from 'widgets/screen-loader/screenLoader.jsx';
28 import LoggingActions from './loggingActions.js';
29 import LoggingStore from './loggingStore.js';
30 import LoggingNav from './loggingNav.jsx';
32 import CategorySeverityGrid from './categorySeverityGrid.jsx';
33 import DenyEventsEditGroup from './denyEventsEditGroup.jsx';
34 import {DropList, RadioButtonGroup, CardSection } from './loggingWidgets.jsx';
36 import Crouton from 'react-crouton';
37 import 'style/common.scss';
40 class DefaultCategorySeverityPanel extends React.Component {
43 const {defaultSeverities, severities, ...props } = this.props;
45 <DashboardCard className="defaultCategorySeverityPanel"
47 title="Syslog Category Severity">
48 <CategorySeverityGrid defaultSeverities={defaultSeverities}
49 severityOptions={severities}/>
56 class LoggingEventsPanel extends React.Component {
58 handleChangeAllowDuplicateEvents(e) {
59 console.log("LoggingEventsPanel.handleChangeAllowDuplicateEvents:");
60 console.log("- e.currentTarget.value=", e.currentTarget.value);
61 console.log("- e.currentTarget=", e.currentTarget);
63 // NOTE, we may want to generalize our string to boolean convert/compare
64 let allowFlag = (e.currentTarget.value == 'true');
65 LoggingStore.updateAllowDuplicateEvents(allowFlag);
68 handleAddDenyEvent(e) {
69 LoggingStore.addDenyEvent(null);
73 const {allowDuplicateEvents, eventIDs, radioItems, ...props} = this.props;
75 let selectedIndex = allowDuplicateEvents ? 0 :1;
78 <DashboardCard className="loggingEventsPanel"
81 <CardSection title={"Duplicates"}>
82 <div className="radioButtonGroupHeader">Allow duplicate events</div>
83 <RadioButtonGroup className="radioButtonGroup"
85 selectedItem={radioItems[selectedIndex]}
86 onChange={this.handleChangeAllowDuplicateEvents}
87 radioGroupName="allowDuplicateEvents"
90 <CardSection title={"Deny"}>
91 <DenyEventsEditGroup className="denyEventsEditGroup"
93 warnInvalidEventID={true}
95 <div className="plusButton" onClick={this.handleAddDenyEvent}>
96 <span className="oi" data-glyph="plus"
97 title="Add event id" aria-hidden="true"></span>
104 LoggingEventsPanel.defaultProps = {
106 { label: "Allow", value: true },
107 { label: "Deny", value: false }
109 allowDuplicateEvents: false,
116 class LoggingGeneralDetailsPanel extends React.Component {
120 syslogViewerURL: props.syslogViewerURL
124 componentWillReceiveProps(props) {
126 syslogViewerURL: props.syslogViewerURL
130 handleUpdateDetailTextField(fieldName) {
134 state[fieldName] = e.target.value;
135 self.setState(state);
139 handleUpdateSyslogViewerURL(e) {
140 LoggingStore.updateSyslogViewerURL(e.target.value);
142 handleOpenSysLogViewerURL(e) {
144 window.open(this.state.syslogViewerURL);
149 <DashboardCard className="loggingGeneralDetailsPanel"
151 title="SysLog Viewer">
152 <div className="section syslogViewerSection">
153 <label className="sectionLabel">Syslog Viewer URL</label>
154 <div className="syslogViewerControls">
155 <input className="textBox" type="text"
156 value={this.state.syslogViewerURL}
157 onBlur={this.handleUpdateSyslogViewerURL}
158 onChange={this.handleUpdateDetailTextField('syslogViewerURL')}
159 placeholder="syslogViewer URL"
161 <div className="goButton"
162 onClick={this.handleOpenSysLogViewerURL.bind(this)}>
164 <span className="oi" data-glyph="arrow-right"
165 title="Add event id" aria-hidden="true"></span>
173 LoggingGeneralDetailsPanel.defaultProps = {
180 * Page level class renders the general logging config page of three panels:
181 * 1. Default severity per category
182 * 2. Events configuration
183 * 3. Syslog viewer setting, launch
185 export default class LoggingGeneral extends React.Component {
189 this.state = LoggingStore.getState();
190 LoggingStore.listen(this.storeListener);
191 this.state.validateErrorEvent = 0;
192 this.state.validateErrorMsg = '';
193 this.state.isLoading = true;
194 this.state.showDumpStateButton = false;
197 storeListener = (state) => {
198 this.setState(state);
202 LoggingStore.getLoggingConfig();
204 isLoading: _isEmpty(this.state.loggingConfig)
207 componentWillUnmount = () => {
208 LoggingStore.unlisten(this.storeListener);
211 componentDidMount() {
212 //console.log("LoggingGeneral.componentDidMount called");
216 componentDidUpdate() {
217 //console.log("LoggingGeneral.componentDidUpdate called");
220 handleSave = (formData, e) => {
223 if (this.validateData()) {
227 LoggingStore.updateLoggingConfig(
228 /* this.collectNulledCategories(
229 this.state.initialLoggingConfig,
230 this.state.loggingConfig),
231 this.removeCategoryNulls(
232 this.state.loggingConfig */
233 this.state.nulledCategories,
235 this.state.loggingConfig
239 console.log("LoggingGeneral.handleSave failed validation");
241 this.context.router.push({pathname: ''});
243 // removeCategoryNulls(config) {
244 // let cleanConfig = _cloneDeep(config);
245 // let cleanSeverities = [];
246 // config.defaultSeverities.map(function(d) {
248 // cleanSeverities.push(d);
251 // cleanConfig.defaultSeverities = cleanSeverities;
252 // return cleanConfig;
254 cleanupConfig(config) {
255 let cleanConfig = _cloneDeep(config);
256 let cleanSeverities = [];
257 cleanConfig.defaultSeverities && cleanConfig.defaultSeverities.map((defSev) => {
258 if (defSev.severity) {
259 cleanSeverities.push(defSev);
262 cleanConfig.defaultSeverities = cleanSeverities;
266 // collectNulledCategories(oldCat, newCat) {
267 // let nulledCategories = [];
268 // let newSeverities = newCat.defaultSeverities;
269 // let oldSeverities = oldCat.defaultSeverities;
270 // newSeverities.map(function(c, i) {
272 // if(oldSeverities[i].severity) {
273 // //verify that categories are the same
274 // if(oldSeverities[i].category == c.category) {
275 // nulledCategories.push({category: c.category})
280 // return nulledCategories;
284 function isEventIdValid(eventID) {
285 // Return true if null, empty string or a number, else return false
286 if (!eventID || eventID.length == 0) {
289 return (isNaN(+eventID)) ? false : true;
293 let invalidEventIDs = [];
294 this.state.loggingConfig.denyEventIDs.forEach(
295 function(eventID, index) {
296 if (!isEventIdValid(eventID)) {
297 invalidEventIDs.push({ eventID: eventID, index: index});
300 if (invalidEventIDs.length > 0) {
301 console.log("invalidEvents = ", invalidEventIDs);
302 if (invalidEventIDs.length == 1) {
303 let msg = 'There is ' + invalidEventIDs.length + ' invalid event ID';
304 this.validateError(msg);
306 let msg = 'There are ' + invalidEventIDs.length + ' invalid event IDs';
307 this.validateError(msg);
309 // How should we identify each invalid value?
316 handleCancel = (e) => {
317 console.log("LoggingGeneral.handleCancel clicked");
319 // TODO: restore original state
320 LoggingStore.resetLoggingConfigData();
321 this.context.router.push({pathname: ''});
325 validateError = (msg) => {
327 validateErrorEvent: true,
328 validateErrorMsg: msg
331 validateReset = () => {
333 validateErrorEvent: false
336 returnCrouton = () => {
337 return <Crouton id={Date.now()}
338 message={this.state.validateErrorMsg}
340 hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
341 onDismiss={this.validateReset}
344 preventDefault = (e) => {
349 renderDumpStateButton() {
357 let errorMessage = this.returnCrouton();
359 let syslogViewerURL = this.state.loggingConfig.syslogviewer;
360 let defaultSeverities = this.state.loggingConfig.defaultSeverities;
361 // NOTE: There are modifications to original code here
362 // for RIFT-14856 so that default severities map to syslog sink
364 // Find first syslog sink with (WTF - no type on sinks!) name syslog.
365 let syslogSink = this.state.loggingConfig.sinks && _find(this.state.loggingConfig.sinks, {
368 let defaultSyslogSeverities = [];
370 this.state.loggingConfig && this.state.loggingConfig.defaultSeverities && this.state.loggingConfig.defaultSeverities.map((defaultSeverity) => {
371 // Mapping between default categories and names inside a sink
372 let syslogFilterCategory = (syslogSink.filter && syslogSink.filter.category && _find(syslogSink.filter.category, {
373 name: defaultSeverity.category
375 name: defaultSeverity.category,
378 defaultSyslogSeverities.push(syslogFilterCategory);
380 let severities = this.state.loggingConfig.severities;
381 let allowDuplicateEvents = this.state.loggingConfig.allowDuplicateEvents;
382 let denyEventIDs = this.state.loggingConfig.denyEventIDs;
384 let dumpStateButton = (
385 <button name="dump_state"
386 onClick={this.handleDumpState}>Dump State</button>);
390 <div className="app-body">
392 (this.state.showLoggingNav) ? <LoggingNav currentPage="General" /> : ''
395 <form className="loggingPage loggingGeneral"
396 onSubmit={this.preventDefault}>
398 <ScreenLoader show={this.state.isLoading}/>
399 <div className="panelContainer">
400 {/*<DefaultCategorySeverityPanel defaultSeverities={defaultSeverities}
401 severities={severities}
403 <DefaultCategorySeverityPanel defaultSeverities={defaultSyslogSeverities}
404 severities={severities}
406 <LoggingEventsPanel allowDuplicateEvents={allowDuplicateEvents}
407 eventIDs={denyEventIDs} />
408 <LoggingGeneralDetailsPanel syslogViewerURL={syslogViewerURL} />
410 <div className="loggingPageFooter">
411 <div className="loggingformButtonGroup">
413 (this.state.showDumpStateButton) ? dumpStateButton : ''
415 <Button className="cancel light" label="Reset"
416 onClick={this.handleCancel} />
417 <Button className="save dark" role="button" label="Save"
418 onClick={this.handleSave.bind(this, true)} />
426 // Dev and debug support
427 handleDumpState = (e) => {
428 console.log("State dump:");
429 console.log("event ids=", this.state.loggingConfig.denyEventIDs);
430 console.log("initial state=", this.state.initialLoggingConfig);
431 console.log("active state=", this.state.loggingConfig);
434 LoggingGeneral.contextTypes = {
435 router: React.PropTypes.object