Fix Dashboard minor UI issues.

Fix the issue where PG logs doesn't display in CSV or JSON format even if it gets selected through the UI.
pull/7685/head
Khushboo Vashi 2024-07-11 10:26:42 +05:30
parent 6b012193e3
commit b303693ae8
5 changed files with 69 additions and 64 deletions

View File

@ -536,16 +536,18 @@ def logs(log_format=None, disp_format=None, sid=None, page=0):
status, _format = g.conn.execute_scalar(sql) status, _format = g.conn.execute_scalar(sql)
# Check the requested format is available or not # Check the requested format is available or not
log_format = ''
if log_format == 'C' and 'csvlog' in _format: if log_format == 'C' and 'csvlog' in _format:
log_format = 'csvlog' log_format = 'csvlog'
elif log_format == 'J' and 'jsonlog' in _format: elif log_format == 'J' and 'jsonlog' in _format:
log_format = 'jsonlog' log_format = 'jsonlog'
else:
log_format = ''
sql = render_template( sql = render_template(
"/".join([g.template_path, 'log_stat.sql']), "/".join([g.template_path, 'log_stat.sql']),
log_format=log_format, conn=g.conn log_format=log_format, conn=g.conn
) )
status, res = g.conn.execute_scalar(sql) status, res = g.conn.execute_scalar(sql)
if not status: if not status:
return internal_server_error(errormsg=res) return internal_server_error(errormsg=res)
@ -557,6 +559,12 @@ def logs(log_format=None, disp_format=None, sid=None, page=0):
file_stat = json.loads(res[0]) file_stat = json.loads(res[0])
if file_stat <= 0:
return ajax_response(
response={'logs_disabled': True},
status=200
)
_start = 0 _start = 0
_end = ON_DEMAND_LOG_COUNT _end = ON_DEMAND_LOG_COUNT
page = int(page) page = int(page)
@ -573,13 +581,12 @@ def logs(log_format=None, disp_format=None, sid=None, page=0):
log_format=log_format, conn=g.conn log_format=log_format, conn=g.conn
) )
status, res = g.conn.execute_dict(sql) status, res = g.conn.execute_dict(sql)
if not status: if not status:
return internal_server_error(errormsg=res) return internal_server_error(errormsg=res)
final_res = res['rows'][0]['pg_read_file'].split('\n') final_res = res['rows'][0]['pg_read_file'].split('\n')
# Json format # Json format
if log_format == 'J': if log_format == 'jsonlog':
for f in final_res: for f in final_res:
try: try:
_tmp_log = json.loads(f) _tmp_log = json.loads(f)
@ -591,7 +598,7 @@ def logs(log_format=None, disp_format=None, sid=None, page=0):
pass pass
# CSV format # CSV format
elif log_format == 'C': elif log_format == 'csvlog':
for f in final_res: for f in final_res:
try: try:
_tmp_log = f.split(',') _tmp_log = f.split(',')

View File

@ -83,15 +83,19 @@ const Root = styled('div')(({theme}) => ({
}, },
'& .Dashboard-textArea': { '& .Dashboard-textArea': {
height: '88%', height: '88%',
} },
'& .RefreshButtons': {
display: 'flex',
},
'& .Mui-disabled': {
pointerEvents: 'auto',
},
}, },
}, },
}, },
'& .Dashboard-emptyPanel': { '& .Dashboard-emptyPanel': {
width: '100%', width: '100%',
height: '100%',
background: theme.otherVars.emptySpaceBg, background: theme.otherVars.emptySpaceBg,
overflow: 'auto',
padding: '8px', padding: '8px',
display: 'flex', display: 'flex',
}, },
@ -234,7 +238,7 @@ function getCancelCell(pgAdmin, sid, did, canTakeAction, onSuccess) {
function CustomRefresh({refresh, setRefresh}) { function CustomRefresh({refresh, setRefresh}) {
return ( return (
<RefreshButton onClick={(e) => { <RefreshButton noBorder={false} onClick={(e) => {
e.preventDefault(); e.preventDefault();
setRefresh(!refresh); setRefresh(!refresh);
}}/> }}/>
@ -245,12 +249,8 @@ CustomRefresh.propTypes = {
setRefresh: PropTypes.func, setRefresh: PropTypes.func,
}; };
function ActiveOnlyHeader({activeOnly, setActiveOnly, refresh, setRefresh}) { function ActiveOnlyHeader({activeOnly, setActiveOnly}) {
return (<Fragment> return (
<RefreshButton onClick={(e) => {
e.preventDefault();
setRefresh(!refresh);
}}/>
<InputCheckbox <InputCheckbox
label={gettext('Active sessions only')} label={gettext('Active sessions only')}
labelPlacement="end" labelPlacement="end"
@ -263,7 +263,7 @@ function ActiveOnlyHeader({activeOnly, setActiveOnly, refresh, setRefresh}) {
controlProps={{ controlProps={{
label: gettext('Active sessions only'), label: gettext('Active sessions only'),
}} }}
/></Fragment> />
); );
} }
ActiveOnlyHeader.propTypes = { ActiveOnlyHeader.propTypes = {
@ -768,7 +768,6 @@ function Dashboard({
enableFilters: true, enableFilters: true,
minSize: 50, minSize: 50,
size: 80, size: 80,
cell: ({ value }) => String(value)
}, },
]; ];
@ -824,8 +823,10 @@ function Dashboard({
let _format = res.data; let _format = res.data;
let _frm = [ let _frm = [
{'label': gettext('Text'), 'value': 'T', 'disabled': !_format.includes('stderr')}, {'label': gettext('Text'), 'value': 'T', 'disabled': !_format.includes('stderr')},
{'label': gettext('JSON'), 'value': 'J', 'disabled': !_format.includes('jsonlog')}, {'label': gettext('JSON'), 'value': 'J', 'disabled': !_format.includes('jsonlog'),
{'label': gettext('CSV'), 'value': 'C', 'disabled': !_format.includes('csvlog')} tooltip: gettext('Enable JSON logging from postgresql.conf.')},
{'label': gettext('CSV'), 'value': 'C', 'disabled': !_format.includes('csvlog'),
tooltip: gettext('Enable CSV logging from postgres.conf.')}
]; ];
setLogConfigFormat(_frm); setLogConfigFormat(_frm);
}) })
@ -840,7 +841,6 @@ function Dashboard({
useEffect(() => { useEffect(() => {
if (mainTabVal == 0) return;
// disable replication tab // disable replication tab
if(!treeNodeInfo?.server?.replication_type && mainTabVal == 5) { if(!treeNodeInfo?.server?.replication_type && mainTabVal == 5) {
setMainTabVal(0); setMainTabVal(0);
@ -879,7 +879,7 @@ function Dashboard({
if (node) { if (node) {
setSsMsg(gettext('Loading logs...')); setSsMsg(gettext('Loading logs...'));
setDashData([]); setDashData([]);
if (mainTabVal != 4 && mainTabVal != 5) { if (mainTabVal == 1 || mainTabVal == 2 || mainTabVal == 3) {
api({ api({
url: url, url: url,
type: 'GET', type: 'GET',
@ -1081,6 +1081,7 @@ function Dashboard({
<TabPanel value={mainTabVal} index={1} classNameRoot='Dashboard-tabPanel'> <TabPanel value={mainTabVal} index={1} classNameRoot='Dashboard-tabPanel'>
{!_.isUndefined(preferences) && preferences.show_activity && ( {!_.isUndefined(preferences) && preferences.show_activity && (
<Fragment> <Fragment>
<CustomRefresh refresh={refresh} setRefresh={setRefresh}/>
<SectionContainer title={gettext('Sessions')} style={{height: 'auto', minHeight: '200px', paddingBottom: '20px'}} <SectionContainer title={gettext('Sessions')} style={{height: 'auto', minHeight: '200px', paddingBottom: '20px'}}
> >
<PgTable <PgTable
@ -1094,7 +1095,6 @@ function Dashboard({
</SectionContainer> </SectionContainer>
<SectionContainer title={gettext('Locks')} style={{height: 'auto', minHeight: '200px', paddingBottom: '20px'}}> <SectionContainer title={gettext('Locks')} style={{height: 'auto', minHeight: '200px', paddingBottom: '20px'}}>
<PgTable <PgTable
customHeader={<CustomRefresh refresh={refresh} setRefresh={setRefresh}/>}
caveTable={false} caveTable={false}
tableNoBorder={false} tableNoBorder={false}
columns={databaseLocksColumns} columns={databaseLocksColumns}
@ -1103,7 +1103,6 @@ function Dashboard({
</SectionContainer> </SectionContainer>
<SectionContainer title={gettext('Prepared Transactions')} style={{height: 'auto', minHeight: '200px', paddingBottom: '20px'}}> <SectionContainer title={gettext('Prepared Transactions')} style={{height: 'auto', minHeight: '200px', paddingBottom: '20px'}}>
<PgTable <PgTable
customHeader={<CustomRefresh refresh={refresh} setRefresh={setRefresh}/>}
caveTable={false} caveTable={false}
tableNoBorder={false} tableNoBorder={false}
columns={databasePreparedColumns} columns={databasePreparedColumns}

View File

@ -23,11 +23,11 @@ const StyledPgIconButton = styled(PgIconButton)(({theme}) => ({
} }
})); }));
export default function RefreshButton({onClick}) { export default function RefreshButton({onClick, noBorder=true}) {
return ( return (
<StyledPgIconButton <StyledPgIconButton
size="xs" size="xs"
noBorder noBorder={noBorder}
className='RefreshButtons' className='RefreshButtons'
icon={<CachedOutlinedIcon />} icon={<CachedOutlinedIcon />}
onClick={onClick} onClick={onClick}
@ -39,5 +39,6 @@ export default function RefreshButton({onClick}) {
} }
RefreshButton.propTypes = { RefreshButton.propTypes = {
onClick: PropTypes.func onClick: PropTypes.func,
noBorder: PropTypes.bool
}; };

View File

@ -676,7 +676,7 @@ export const InputToggle = forwardRef(({ cid, value, onChange, options, disabled
const isDisabled = disabled || option.disabled || (readonly && !isSelected); const isDisabled = disabled || option.disabled || (readonly && !isSelected);
return <ToggleCheckButton ref={i == 0 ? ref : null} key={option.label} label={option.label} return <ToggleCheckButton ref={i == 0 ? ref : null} key={option.label} label={option.label}
selected={isSelected} value={option.value} disabled={isDisabled} selected={isSelected} value={option.value} disabled={isDisabled} title={option.tooltip}
/>; />;
}) })
} }

View File

@ -118,7 +118,6 @@ export function Table({ columns, data, hasSelectRow, schema, sortOptions, tableP
let totalFetched = 0; let totalFetched = 0;
let totalDBRowCount = 0; let totalDBRowCount = 0;
if (loadNextPage) {
//Infinite scrolling //Infinite scrolling
const { _data, fetchNextPage, isFetching } = const { _data, fetchNextPage, isFetching } =
useInfiniteQuery({ useInfiniteQuery({
@ -157,7 +156,6 @@ export function Table({ columns, data, hasSelectRow, schema, sortOptions, tableP
React.useEffect(() => { React.useEffect(() => {
fetchMoreOnBottomReached(tableRef.current); fetchMoreOnBottomReached(tableRef.current);
}, [fetchMoreOnBottomReached]); }, [fetchMoreOnBottomReached]);
}
const table = useReactTable({ const table = useReactTable({
columns: finalColumns, columns: finalColumns,