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 _ from 'lodash';
20 import './logging.scss';
22 import Button from 'widgets/button/rw.button.js';
23 import DashboardCard from 'widgets/dashboard_card/dashboard_card.jsx';
24 import ScreenLoader from 'widgets/screen-loader/screenLoader.jsx';
26 import LoggingActions from './loggingActions.js';
27 import LoggingStore from './loggingStore.js';
28 import LoggingNav from './loggingNav.jsx';
30 import CategorySeverityGrid from './categorySeverityGrid.jsx';
31 import DenyEventsEditGroup from './denyEventsEditGroup.jsx';
32 import {DropList, RadioButtonGroup, CardSection } from './loggingWidgets.jsx';
34 import Crouton from 'react-crouton';
35 import 'style/common.scss';
38 class DefaultCategorySeverityPanel extends React.Component {
41 const {defaultSeverities, severities, ...props } = this.props;
43 <DashboardCard className="defaultCategorySeverityPanel"
45 title="Syslog Category Severity">
46 <CategorySeverityGrid defaultSeverities={defaultSeverities}
47 severityOptions={severities}/>
54 class LoggingEventsPanel extends React.Component {
56 handleChangeAllowDuplicateEvents(e) {
57 console.log("LoggingEventsPanel.handleChangeAllowDuplicateEvents:");
58 console.log("- e.currentTarget.value=", e.currentTarget.value);
59 console.log("- e.currentTarget=", e.currentTarget);
61 // NOTE, we may want to generalize our string to boolean convert/compare
62 let allowFlag = (e.currentTarget.value == 'true');
63 LoggingStore.updateAllowDuplicateEvents(allowFlag);
66 handleAddDenyEvent(e) {
67 LoggingStore.addDenyEvent(null);
71 const {allowDuplicateEvents, eventIDs, radioItems, ...props} = this.props;
73 let selectedIndex = allowDuplicateEvents ? 0 :1;
76 <DashboardCard className="loggingEventsPanel"
79 <CardSection title={"Duplicates"}>
80 <div className="radioButtonGroupHeader">Allow duplicate events</div>
81 <RadioButtonGroup className="radioButtonGroup"
83 selectedItem={radioItems[selectedIndex]}
84 onChange={this.handleChangeAllowDuplicateEvents}
85 radioGroupName="allowDuplicateEvents"
88 <CardSection title={"Deny"}>
89 <DenyEventsEditGroup className="denyEventsEditGroup"
91 warnInvalidEventID={true}
93 <div className="plusButton" onClick={this.handleAddDenyEvent}>
94 <span className="oi" data-glyph="plus"
95 title="Add event id" aria-hidden="true"></span>
102 LoggingEventsPanel.defaultProps = {
104 { label: "Allow", value: true },
105 { label: "Deny", value: false }
107 allowDuplicateEvents: false,
114 class LoggingGeneralDetailsPanel extends React.Component {
118 syslogViewerURL: props.syslogViewerURL
122 componentWillReceiveProps(props) {
124 syslogViewerURL: props.syslogViewerURL
128 handleUpdateDetailTextField(fieldName) {
132 state[fieldName] = e.target.value;
133 self.setState(state);
137 handleUpdateSyslogViewerURL(e) {
138 LoggingStore.updateSyslogViewerURL(e.target.value);
140 handleOpenSysLogViewerURL(e) {
142 window.open(this.state.syslogViewerURL);
147 <DashboardCard className="loggingGeneralDetailsPanel"
149 title="SysLog Viewer">
150 <div className="section syslogViewerSection">
151 <label className="sectionLabel">Syslog Viewer URL</label>
152 <div className="syslogViewerControls">
153 <input className="textBox" type="text"
154 value={this.state.syslogViewerURL}
155 onBlur={this.handleUpdateSyslogViewerURL}
156 onChange={this.handleUpdateDetailTextField('syslogViewerURL')}
157 placeholder="syslogViewer URL"
159 <div className="goButton"
160 onClick={this.handleOpenSysLogViewerURL.bind(this)}>
162 <span className="oi" data-glyph="arrow-right"
163 title="Add event id" aria-hidden="true"></span>
171 LoggingGeneralDetailsPanel.defaultProps = {
178 * Page level class renders the general logging config page of three panels:
179 * 1. Default severity per category
180 * 2. Events configuration
181 * 3. Syslog viewer setting, launch
183 export default class LoggingGeneral extends React.Component {
187 this.state = LoggingStore.getState();
188 LoggingStore.listen(this.storeListener);
189 this.state.validateErrorEvent = 0;
190 this.state.validateErrorMsg = '';
191 this.state.isLoading = true;
192 this.state.showDumpStateButton = false;
195 storeListener = (state) => {
196 this.setState(state);
200 LoggingStore.getLoggingConfig();
202 isLoading: _.isEmpty(this.state.loggingConfig)
205 componentWillUnmount = () => {
206 LoggingStore.unlisten(this.storeListener);
209 componentDidMount() {
210 //console.log("LoggingGeneral.componentDidMount called");
214 componentDidUpdate() {
215 //console.log("LoggingGeneral.componentDidUpdate called");
218 handleSave = (formData, e) => {
221 if (this.validateData()) {
225 LoggingStore.updateLoggingConfig(
226 /* this.collectNulledCategories(
227 this.state.initialLoggingConfig,
228 this.state.loggingConfig),
229 this.removeCategoryNulls(
230 this.state.loggingConfig */
231 this.state.nulledCategories,
233 this.state.loggingConfig
237 console.log("LoggingGeneral.handleSave failed validation");
239 this.context.router.push({pathname: ''});
241 // removeCategoryNulls(config) {
242 // let cleanConfig = _.cloneDeep(config);
243 // let cleanSeverities = [];
244 // config.defaultSeverities.map(function(d) {
246 // cleanSeverities.push(d);
249 // cleanConfig.defaultSeverities = cleanSeverities;
250 // return cleanConfig;
252 cleanupConfig(config) {
253 let cleanConfig = _.cloneDeep(config);
254 let cleanSeverities = [];
255 cleanConfig.defaultSeverities && cleanConfig.defaultSeverities.map((defSev) => {
256 if (defSev.severity) {
257 cleanSeverities.push(defSev);
260 cleanConfig.defaultSeverities = cleanSeverities;
264 // collectNulledCategories(oldCat, newCat) {
265 // let nulledCategories = [];
266 // let newSeverities = newCat.defaultSeverities;
267 // let oldSeverities = oldCat.defaultSeverities;
268 // newSeverities.map(function(c, i) {
270 // if(oldSeverities[i].severity) {
271 // //verify that categories are the same
272 // if(oldSeverities[i].category == c.category) {
273 // nulledCategories.push({category: c.category})
278 // return nulledCategories;
282 function isEventIdValid(eventID) {
283 // Return true if null, empty string or a number, else return false
284 if (!eventID || eventID.length == 0) {
287 return (isNaN(+eventID)) ? false : true;
291 let invalidEventIDs = [];
292 this.state.loggingConfig.denyEventIDs.forEach(
293 function(eventID, index) {
294 if (!isEventIdValid(eventID)) {
295 invalidEventIDs.push({ eventID: eventID, index: index});
298 if (invalidEventIDs.length > 0) {
299 console.log("invalidEvents = ", invalidEventIDs);
300 if (invalidEventIDs.length == 1) {
301 let msg = 'There is ' + invalidEventIDs.length + ' invalid event ID';
302 this.validateError(msg);
304 let msg = 'There are ' + invalidEventIDs.length + ' invalid event IDs';
305 this.validateError(msg);
307 // How should we identify each invalid value?
314 handleCancel = (e) => {
315 console.log("LoggingGeneral.handleCancel clicked");
317 // TODO: restore original state
318 LoggingStore.resetLoggingConfigData();
319 this.context.router.push({pathname: ''});
323 validateError = (msg) => {
325 validateErrorEvent: true,
326 validateErrorMsg: msg
329 validateReset = () => {
331 validateErrorEvent: false
334 returnCrouton = () => {
335 return <Crouton id={Date.now()}
336 message={this.state.validateErrorMsg}
338 hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
339 onDismiss={this.validateReset}
342 preventDefault = (e) => {
347 renderDumpStateButton() {
355 let errorMessage = this.returnCrouton();
357 let syslogViewerURL = this.state.loggingConfig.syslogviewer;
358 let defaultSeverities = this.state.loggingConfig.defaultSeverities;
359 // NOTE: There are modifications to original code here
360 // for RIFT-14856 so that default severities map to syslog sink
362 // Find first syslog sink with (WTF - no type on sinks!) name syslog.
363 let syslogSink = this.state.loggingConfig.sinks && _.find(this.state.loggingConfig.sinks, {
366 let defaultSyslogSeverities = [];
368 this.state.loggingConfig && this.state.loggingConfig.defaultSeverities && this.state.loggingConfig.defaultSeverities.map((defaultSeverity) => {
369 // Mapping between default categories and names inside a sink
370 let syslogFilterCategory = (syslogSink.filter && syslogSink.filter.category && _.find(syslogSink.filter.category, {
371 name: defaultSeverity.category
373 name: defaultSeverity.category,
376 defaultSyslogSeverities.push(syslogFilterCategory);
378 let severities = this.state.loggingConfig.severities;
379 let allowDuplicateEvents = this.state.loggingConfig.allowDuplicateEvents;
380 let denyEventIDs = this.state.loggingConfig.denyEventIDs;
382 let dumpStateButton = (
383 <button name="dump_state"
384 onClick={this.handleDumpState}>Dump State</button>);
388 <div className="app-body">
390 (this.state.showLoggingNav) ? <LoggingNav currentPage="General" /> : ''
393 <form className="loggingPage loggingGeneral"
394 onSubmit={this.preventDefault}>
396 <ScreenLoader show={this.state.isLoading}/>
397 <div className="panelContainer">
398 {/*<DefaultCategorySeverityPanel defaultSeverities={defaultSeverities}
399 severities={severities}
401 <DefaultCategorySeverityPanel defaultSeverities={defaultSyslogSeverities}
402 severities={severities}
404 <LoggingEventsPanel allowDuplicateEvents={allowDuplicateEvents}
405 eventIDs={denyEventIDs} />
406 <LoggingGeneralDetailsPanel syslogViewerURL={syslogViewerURL} />
408 <div className="loggingPageFooter">
409 <div className="loggingformButtonGroup">
411 (this.state.showDumpStateButton) ? dumpStateButton : ''
413 <Button className="cancel light" label="Reset"
414 onClick={this.handleCancel} />
415 <Button className="save dark" role="button" label="Save"
416 onClick={this.handleSave.bind(this, true)} />
424 // Dev and debug support
425 handleDumpState = (e) => {
426 console.log("State dump:");
427 console.log("event ids=", this.state.loggingConfig.denyEventIDs);
428 console.log("initial state=", this.state.initialLoggingConfig);
429 console.log("active state=", this.state.loggingConfig);
432 LoggingGeneral.contextTypes = {
433 router: React.PropTypes.object