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

View File

@@ -83,15 +83,19 @@ const Root = styled('div')(({theme}) => ({
},
'& .Dashboard-textArea': {
height: '88%',
}
},
'& .RefreshButtons': {
display: 'flex',
},
'& .Mui-disabled': {
pointerEvents: 'auto',
},
},
},
},
'& .Dashboard-emptyPanel': {
width: '100%',
height: '100%',
background: theme.otherVars.emptySpaceBg,
overflow: 'auto',
padding: '8px',
display: 'flex',
},
@@ -234,7 +238,7 @@ function getCancelCell(pgAdmin, sid, did, canTakeAction, onSuccess) {
function CustomRefresh({refresh, setRefresh}) {
return (
<RefreshButton onClick={(e) => {
<RefreshButton noBorder={false} onClick={(e) => {
e.preventDefault();
setRefresh(!refresh);
}}/>
@@ -245,12 +249,8 @@ CustomRefresh.propTypes = {
setRefresh: PropTypes.func,
};
function ActiveOnlyHeader({activeOnly, setActiveOnly, refresh, setRefresh}) {
return (<Fragment>
<RefreshButton onClick={(e) => {
e.preventDefault();
setRefresh(!refresh);
}}/>
function ActiveOnlyHeader({activeOnly, setActiveOnly}) {
return (
<InputCheckbox
label={gettext('Active sessions only')}
labelPlacement="end"
@@ -263,7 +263,7 @@ function ActiveOnlyHeader({activeOnly, setActiveOnly, refresh, setRefresh}) {
controlProps={{
label: gettext('Active sessions only'),
}}
/></Fragment>
/>
);
}
ActiveOnlyHeader.propTypes = {
@@ -768,7 +768,6 @@ function Dashboard({
enableFilters: true,
minSize: 50,
size: 80,
cell: ({ value }) => String(value)
},
];
@@ -824,8 +823,10 @@ function Dashboard({
let _format = res.data;
let _frm = [
{'label': gettext('Text'), 'value': 'T', 'disabled': !_format.includes('stderr')},
{'label': gettext('JSON'), 'value': 'J', 'disabled': !_format.includes('jsonlog')},
{'label': gettext('CSV'), 'value': 'C', 'disabled': !_format.includes('csvlog')}
{'label': gettext('JSON'), 'value': 'J', 'disabled': !_format.includes('jsonlog'),
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);
})
@@ -840,7 +841,6 @@ function Dashboard({
useEffect(() => {
if (mainTabVal == 0) return;
// disable replication tab
if(!treeNodeInfo?.server?.replication_type && mainTabVal == 5) {
setMainTabVal(0);
@@ -879,7 +879,7 @@ function Dashboard({
if (node) {
setSsMsg(gettext('Loading logs...'));
setDashData([]);
if (mainTabVal != 4 && mainTabVal != 5) {
if (mainTabVal == 1 || mainTabVal == 2 || mainTabVal == 3) {
api({
url: url,
type: 'GET',
@@ -1081,6 +1081,7 @@ function Dashboard({
<TabPanel value={mainTabVal} index={1} classNameRoot='Dashboard-tabPanel'>
{!_.isUndefined(preferences) && preferences.show_activity && (
<Fragment>
<CustomRefresh refresh={refresh} setRefresh={setRefresh}/>
<SectionContainer title={gettext('Sessions')} style={{height: 'auto', minHeight: '200px', paddingBottom: '20px'}}
>
<PgTable
@@ -1094,7 +1095,6 @@ function Dashboard({
</SectionContainer>
<SectionContainer title={gettext('Locks')} style={{height: 'auto', minHeight: '200px', paddingBottom: '20px'}}>
<PgTable
customHeader={<CustomRefresh refresh={refresh} setRefresh={setRefresh}/>}
caveTable={false}
tableNoBorder={false}
columns={databaseLocksColumns}
@@ -1103,7 +1103,6 @@ function Dashboard({
</SectionContainer>
<SectionContainer title={gettext('Prepared Transactions')} style={{height: 'auto', minHeight: '200px', paddingBottom: '20px'}}>
<PgTable
customHeader={<CustomRefresh refresh={refresh} setRefresh={setRefresh}/>}
caveTable={false}
tableNoBorder={false}
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 (
<StyledPgIconButton
size="xs"
noBorder
noBorder={noBorder}
className='RefreshButtons'
icon={<CachedOutlinedIcon />}
onClick={onClick}
@@ -39,5 +39,6 @@ export default function RefreshButton({onClick}) {
}
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);
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,46 +118,44 @@ export function Table({ columns, data, hasSelectRow, schema, sortOptions, tableP
let totalFetched = 0;
let totalDBRowCount = 0;
if (loadNextPage) {
//Infinite scrolling
const { _data, fetchNextPage, isFetching } =
useInfiniteQuery({
queryKey: ['logs'],
queryFn: async () => {
const fetchedData = await loadNextPage();
return fetchedData;
},
initialPageParam: 0,
getNextPageParam: (_lastGroup, groups) => groups.length,
refetchOnWindowFocus: false,
placeholderData: keepPreviousData,
});
flatData = _data || [];
totalFetched = flatData.length;
//called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table
fetchMoreOnBottomReached = React.useCallback(
(containerRefElement = HTMLDivElement | null) => {
if (containerRefElement) {
const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
//once the user has scrolled within 500px of the bottom of the table, fetch more data if we can
if (
scrollHeight - scrollTop - clientHeight < 500 &&
!isFetching
) {
fetchNextPage();
}
}
//Infinite scrolling
const { _data, fetchNextPage, isFetching } =
useInfiniteQuery({
queryKey: ['logs'],
queryFn: async () => {
const fetchedData = await loadNextPage();
return fetchedData;
},
[fetchNextPage, isFetching, totalFetched, totalDBRowCount]
);
initialPageParam: 0,
getNextPageParam: (_lastGroup, groups) => groups.length,
refetchOnWindowFocus: false,
placeholderData: keepPreviousData,
});
//a check on mount and after a fetch to see if the table is already scrolled to the bottom and immediately needs to fetch more data
React.useEffect(() => {
fetchMoreOnBottomReached(tableRef.current);
}, [fetchMoreOnBottomReached]);
}
flatData = _data || [];
totalFetched = flatData.length;
//called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table
fetchMoreOnBottomReached = React.useCallback(
(containerRefElement = HTMLDivElement | null) => {
if (containerRefElement) {
const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
//once the user has scrolled within 500px of the bottom of the table, fetch more data if we can
if (
scrollHeight - scrollTop - clientHeight < 500 &&
!isFetching
) {
fetchNextPage();
}
}
},
[fetchNextPage, isFetching, totalFetched, totalDBRowCount]
);
//a check on mount and after a fetch to see if the table is already scrolled to the bottom and immediately needs to fetch more data
React.useEffect(() => {
fetchMoreOnBottomReached(tableRef.current);
}, [fetchMoreOnBottomReached]);
const table = useReactTable({
columns: finalColumns,