Skip to content

Commit

Permalink
Making the ServerLogs component
Browse files Browse the repository at this point in the history
WIP

WIP

Searchbox working

WIP

WIP

WIP

w

f

WIP

r

t
  • Loading branch information
ananya-agarwal committed Dec 24, 2024
1 parent ca87369 commit 86d277d
Show file tree
Hide file tree
Showing 10 changed files with 350 additions and 207 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Licensed to Cloudera, Inc. under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. Cloudera, Inc. 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.


@import '../../../components/styles/variables';

.antd.cuix {
.server-admin-header {
display: flex;
align-items: center;
justify-content: space-between;

.server__input-filter {
margin: $font-size-sm;
width: 200px;
padding: 2px;

input {
box-shadow: none;
-webkit-box-shadow: none;
margin-top: 3px;
}

.server__input-filter--prefix {
margin: 5px 3px 3px 3px;
}
}

.server__filter-arrow {
height: 16px;
width: 16px;
margin: 0;
}

.server--right-actions {
display: flex;
align-items: center;
justify-content: space-between;

.server__wrap-logs,
.server__download-button,
.server__host-text {
margin-right: 8px;
}

.server__checkbox-icon {
margin-right: 2px;
}

.server__download-button {
border: 1px solid $fluidx-gray-600;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Licensed to Cloudera, Inc. under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. Cloudera, Inc. 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.

import React, { useState } from 'react';
import { Input, Checkbox } from 'antd';
import Button from 'cuix/dist/components/Button';
import Search from '@cloudera/cuix-core/icons/react/SearchIcon';
import Download from '@cloudera/cuix-core/icons/react/DownloadIcon';
import { i18nReact } from '../../../utils/i18nReact';
import huePubSub from '../../../utils/huePubSub';
import './ServerLogsHeader.scss';

interface ServerLogsHeaderProps {
onFilterChange: (value: string) => void;
onWrapLogsChange: (wrap: boolean) => void;
hostName: string;
}

const ServerLogsHeader: React.FC<ServerLogsHeaderProps> = ({
onFilterChange,
onWrapLogsChange,
hostName
}): JSX.Element => {
const { t } = i18nReact.useTranslation();
const [filterValue, setFilterValue] = useState('');
const [wrapLogs, setWrapLogs] = useState(true);

const handleFilterChange = (newFilterValue: string) => {
setFilterValue(newFilterValue);
onFilterChange(newFilterValue);
};

const handleDownloadClick = () => {
huePubSub.publish('open.link', '/desktop/download_logs');
};

return (
<div className="server-admin-header admin-header">
<Input
className="server__input-filter"
placeholder={t('Search in the logs')}
prefix={
<span className="server__input-filter--prefix">
<Search />
</span>
}
value={filterValue}
onChange={e => handleFilterChange(e.target.value)}
/>

<div className="server--right-actions">
<span className="server__host-text">{t(`Host: ${hostName}`)}</span>
<Checkbox
onChange={e => {
setWrapLogs(e.target.checked);
onWrapLogsChange(e.target.checked);
}}
checked={wrapLogs}
className="server__checkbox-icon"
/>
<span className="server__wrap-logs">Wrap logs</span>
<Button
className="server__download-button"
data-event="download-button"
icon={<Download />}
onClick={handleDownloadClick}
>
{t('Download entire log as zip')}
</Button>
</div>
</div>
);
};

export default ServerLogsHeader;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Licensed to Cloudera, Inc. under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. Cloudera, Inc. 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.

@import '../../../components/styles/variables';

.antd.cuix {
.server-logs-component {
background-color: $fluidx-gray-100;
padding: 24px;

.server__display-logs {
overflow: auto;
background-color: $fluidx-white;
width: 100%;
padding: 10px 0 10px 0;

.server__log-line {
background-color: $fluidx-white;
margin: 0;
padding: 2px;
}
}

.server_nowrap {
white-space: nowrap;
}

.server__no-logs-found {
background-color: $fluidx-white;
}

.server--highlight-word {
background-color: $fluidx-pear-050;
color: $fluidx-black;
}
}
}
131 changes: 131 additions & 0 deletions desktop/core/src/desktop/js/apps/admin/ServerLogs/ServerLogsTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Licensed to Cloudera, Inc. under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. Cloudera, Inc. 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.

import React, { useState, useEffect } from 'react';
import { Spin, Alert } from 'antd';
import ServerLogsHeader from './ServerLogsHeader';
import './ServerLogsTab.scss';

const ServerLogs: React.FC = (): JSX.Element => {
const [loading, setLoading] = useState(true);
const [logs, setLogs] = useState<string[]>([]);
const [noLogsFound, setNoLogsFound] = useState(false);
const [error, setError] = useState<string>('');
const [filter, setFilter] = useState<string>('');
const [hostName, setHostName] = useState<string>('');
const [wrapLogs, setWrapLogs] = useState(true);

useEffect(() => {
const fetchLogs = async () => {
try {
const response = await fetch('/api/v1/logs');
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const json = await response.json();
const fetchedLogs = json['logs'];
if (!fetchedLogs.length || fetchedLogs[0] === '') {
setNoLogsFound(true);
} else {
setLogs(fetchedLogs);
}
setHostName(json['hue_hostname']);
} catch (error) {
setError(error.message);
} finally {
setLoading(false);
}
};
fetchLogs();
}, []);

useEffect(() => {
const updateSize = () => {
const newHeight = document.documentElement.clientHeight - 250;
const logsComponent = document.querySelector('.server__display-logs') as HTMLElement;
if (logsComponent) {
logsComponent.style.height = `${newHeight}px`;
}
};
updateSize();
window.addEventListener('resize', updateSize);
return () => window.removeEventListener('resize', updateSize);
}, []);

const handleFilterChange = (newFilterValue: string) => {
setFilter(newFilterValue);
};

const handleWrapLogsChange = (wrap: boolean) => {
setWrapLogs(wrap);
};

const highlightText = (text: string, searchValue: string) => {
if (!searchValue) {
return text;
}
const regex = new RegExp(`(${searchValue})`, 'gi');
const parts = text.split(regex);
return parts.map((part, index) =>
regex.test(part) ? (
<mark key={index} className="server--highlight-word">
{part}
</mark>
) : (
part
)
);
};

if (error) {
return (
<div className="server-logs-component">
<Alert
message={`Error: ${error}`}
description="An error occurred while fetching server logs."
type="error"
/>
</div>
);
}

return (
<div className="server-logs-component">
<Spin spinning={loading}>
{!loading && (
<>
<ServerLogsHeader
onFilterChange={handleFilterChange}
onWrapLogsChange={handleWrapLogsChange}
hostName={hostName}
/>
{noLogsFound && <pre className="server__no-logs-found">No logs found!</pre>}

<div className="server__display-logs">
{logs.map((line, index) => (
<div className={`server__log-line ${wrapLogs ? 'server_nowrap' : ''}`} key={index}>
{highlightText(line, filter)}
</div>
))}
</div>
</>
)}
</Spin>
</div>
);
};

export default ServerLogs;
7 changes: 2 additions & 5 deletions desktop/core/src/desktop/js/onePageViewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -461,8 +461,8 @@ class OnePageViewModel {
self.extraEmbeddableURLParams('');
const currentPath = window.location.pathname; // Retrieve the current path from the window location
const basePath = currentPath.split('=')[0];
const inlineScriptsUrls = ['oozie', 'beeswax', 'jobbrowser', 'jobsub', 'logs'].some(
segment => basePath.includes(segment)
const inlineScriptsUrls = ['oozie', 'beeswax', 'jobbrowser', 'jobsub'].some(segment =>
basePath.includes(segment)
);
if (inlineScriptsUrls) {
self.processHeaders(response).done($rawHtml => {
Expand Down Expand Up @@ -640,9 +640,6 @@ class OnePageViewModel {
url: '/desktop/metrics',
app: function () {
self.loadApp('metrics');
self.getActiveAppViewModel(viewModel => {
viewModel.fetchMetrics();
});
}
},
{
Expand Down
3 changes: 3 additions & 0 deletions desktop/core/src/desktop/js/reactComponents/imports.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export async function loadComponent(name) {
case 'Configuration':
return (await import('../apps/admin/Configuration/ConfigurationTab')).default;

case 'ServerLogs':
return (await import('../apps/admin/ServerLogs/ServerLogsTab')).default;

// Application global components here
case 'AppBanner':
return (await import('./AppBanner/AppBanner')).default;
Expand Down
3 changes: 2 additions & 1 deletion desktop/core/src/desktop/log/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ def get_hue_logs(request):
# Read the previous log file contents
buffer = _read_previous_log_file(LOG_BUFFER_SIZE, previous_log_file, prev_log_file_size, log_file_size) + buffer

response = {'hue_hostname': socket.gethostname(), 'logs': ''.join(buffer)}

response = {'hue_hostname': socket.gethostname(), 'logs': buffer[::-1]}
return JsonResponse(response)


Expand Down
3 changes: 3 additions & 0 deletions desktop/core/src/desktop/static/desktop/js/logs-inline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(function () {
window.createReactComponents('#ServerLogs');
})();
Loading

0 comments on commit 86d277d

Please sign in to comment.