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
parent
6b012193e3
commit
b303693ae8
|
@ -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(',')
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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}
|
||||||
/>;
|
/>;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,46 +118,44 @@ 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({
|
queryKey: ['logs'],
|
||||||
queryKey: ['logs'],
|
queryFn: async () => {
|
||||||
queryFn: async () => {
|
const fetchedData = await loadNextPage();
|
||||||
const fetchedData = await loadNextPage();
|
return fetchedData;
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[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
|
flatData = _data || [];
|
||||||
React.useEffect(() => {
|
totalFetched = flatData.length;
|
||||||
fetchMoreOnBottomReached(tableRef.current);
|
|
||||||
}, [fetchMoreOnBottomReached]);
|
//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({
|
const table = useReactTable({
|
||||||
columns: finalColumns,
|
columns: finalColumns,
|
||||||
|
|
Loading…
Reference in New Issue