Skip to content
Snippets Groups Projects
Commit 36ff6f33 authored by Miguel Rincon's avatar Miguel Rincon
Browse files

Logs container uses the whole screen

Calculate the height of the log container using viewport units
to set the height it should have.
parent 7ebbf830
Branches 210502-restore-full-height-of-logs-explorer-2
No related tags found
No related merge requests found
Showing
with 193 additions and 107 deletions
Loading
Loading
@@ -56,7 +56,6 @@ export default {
required: true,
},
},
traceHeight: 600,
data() {
return {
isElasticStackCalloutDismissed: false,
Loading
Loading
@@ -94,6 +93,9 @@ export default {
'showEnvironment',
'fetchEnvironments',
'fetchMoreLogsPrepend',
'dismissRequestEnvironmentsError',
'dismissInvalidTimeRangeWarning',
'dismissRequestLogsError',
]),
 
isCurrentEnvironment(envName) {
Loading
Loading
@@ -115,7 +117,7 @@ export default {
};
</script>
<template>
<div class="environment-logs-viewer mt-3">
<div class="environment-logs-viewer d-flex flex-column py-3">
<gl-alert
v-if="shouldShowElasticStackCallout"
class="mb-3 js-elasticsearch-alert"
Loading
Loading
@@ -132,6 +134,31 @@ export default {
</strong>
</a>
</gl-alert>
<gl-alert
v-if="environments.fetchError"
class="mb-3"
variant="danger"
@dismiss="dismissRequestEnvironmentsError"
>
{{ s__('Metrics|There was an error fetching the environments data, please try again') }}
</gl-alert>
<gl-alert
v-if="timeRange.invalidWarning"
class="mb-3"
variant="warning"
@dismiss="dismissInvalidTimeRangeWarning"
>
{{ s__('Metrics|Invalid time range, please verify.') }}
</gl-alert>
<gl-alert
v-if="logs.fetchError"
class="mb-3"
variant="danger"
@dismiss="dismissRequestLogsError"
>
{{ s__('Environments|There was an error fetching the logs. Please try again.') }}
</gl-alert>
<div class="top-bar d-md-flex border bg-secondary-50 pt-2 pr-1 pb-0 pl-2">
<div class="flex-grow-0">
<gl-dropdown
Loading
Loading
@@ -183,16 +210,15 @@ export default {
 
<gl-infinite-scroll
ref="infiniteScroll"
class="log-lines"
:style="{ height: `${$options.traceHeight}px` }"
:max-list-height="$options.traceHeight"
class="log-lines overflow-auto flex-grow-1 min-height-0"
:fetched-items="logs.lines.length"
@topReached="topReached"
@scroll="scroll"
>
<template #items>
<pre
class="build-trace js-log-trace"
ref="logTrace"
class="build-trace"
><code class="bash js-build-output"><div v-if="showLoader" class="build-loader-animation js-build-loader-animation">
<div class="dot"></div>
<div class="dot"></div>
Loading
Loading
@@ -205,7 +231,7 @@ export default {
></template>
</gl-infinite-scroll>
 
<div ref="logFooter" class="log-footer py-2 px-3">
<div ref="logFooter" class="py-2 px-3 text-white bg-secondary-900">
<gl-sprintf :message="s__('Environments|Logs from %{start} to %{end}.')">
<template #start>{{ timeRange.current.start | formatDate }}</template>
<template #end>{{ timeRange.current.end | formatDate }}</template>
Loading
Loading
import { backOff } from '~/lib/utils/common_utils';
import httpStatusCodes from '~/lib/utils/http_status';
import axios from '~/lib/utils/axios_utils';
import flash from '~/flash';
import { s__ } from '~/locale';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
 
import * as types from './mutation_types';
 
const flashTimeRangeWarning = () => {
flash(s__('Metrics|Invalid time range, please verify.'), 'warning');
};
const flashLogsError = () => {
flash(s__('Metrics|There was an error fetching the logs, please try again'));
};
const requestUntilData = (url, params) =>
backOff((next, stop) => {
axios
Loading
Loading
@@ -31,7 +21,7 @@ const requestUntilData = (url, params) =>
});
});
 
const requestLogsUntilData = state => {
const requestLogsUntilData = ({ commit, state }) => {
const params = {};
const { logs_api_path } = state.environments.options.find(
({ name }) => name === state.environments.current,
Loading
Loading
@@ -49,7 +39,7 @@ const requestLogsUntilData = state => {
params.start_time = start;
params.end_time = end;
} catch {
flashTimeRangeWarning();
commit(types.SHOW_TIME_RANGE_INVALID_WARNING);
}
}
if (state.logs.cursor) {
Loading
Loading
@@ -101,26 +91,22 @@ export const fetchEnvironments = ({ commit, dispatch }, environmentsPath) => {
})
.catch(() => {
commit(types.RECEIVE_ENVIRONMENTS_DATA_ERROR);
flash(s__('Metrics|There was an error fetching the environments data, please try again'));
});
};
 
export const fetchLogs = ({ commit, state }) => {
commit(types.REQUEST_LOGS_DATA);
 
return requestLogsUntilData(state)
return requestLogsUntilData({ commit, state })
.then(({ data }) => {
const { pod_name, pods, logs, cursor } = data;
commit(types.RECEIVE_LOGS_DATA_SUCCESS, { logs, cursor });
commit(types.SET_CURRENT_POD_NAME, pod_name);
commit(types.RECEIVE_PODS_DATA_SUCCESS, pods);
})
.catch(() => {
commit(types.RECEIVE_PODS_DATA_ERROR);
commit(types.RECEIVE_LOGS_DATA_ERROR);
flashLogsError();
});
};
 
Loading
Loading
@@ -132,16 +118,27 @@ export const fetchMoreLogsPrepend = ({ commit, state }) => {
 
commit(types.REQUEST_LOGS_DATA_PREPEND);
 
return requestLogsUntilData(state)
return requestLogsUntilData({ commit, state })
.then(({ data }) => {
const { logs, cursor } = data;
commit(types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS, { logs, cursor });
})
.catch(() => {
commit(types.RECEIVE_LOGS_DATA_PREPEND_ERROR);
flashLogsError();
});
};
 
export const dismissRequestEnvironmentsError = ({ commit }) => {
commit(types.HIDE_REQUEST_ENVIRONMENTS_ERROR);
};
export const dismissRequestLogsError = ({ commit }) => {
commit(types.HIDE_REQUEST_LOGS_ERROR);
};
export const dismissInvalidTimeRangeWarning = ({ commit }) => {
commit(types.HIDE_TIME_RANGE_INVALID_WARNING);
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
export const SET_PROJECT_ENVIRONMENT = 'SET_PROJECT_ENVIRONMENT';
export const SET_SEARCH = 'SET_SEARCH';
export const SET_TIME_RANGE = 'SET_TIME_RANGE';
export const SHOW_TIME_RANGE_INVALID_WARNING = 'SHOW_TIME_RANGE_INVALID_WARNING';
export const HIDE_TIME_RANGE_INVALID_WARNING = 'HIDE_TIME_RANGE_INVALID_WARNING';
export const SET_CURRENT_POD_NAME = 'SET_CURRENT_POD_NAME';
 
export const REQUEST_ENVIRONMENTS_DATA = 'REQUEST_ENVIRONMENTS_DATA';
export const RECEIVE_ENVIRONMENTS_DATA_SUCCESS = 'RECEIVE_ENVIRONMENTS_DATA_SUCCESS';
export const RECEIVE_ENVIRONMENTS_DATA_ERROR = 'RECEIVE_ENVIRONMENTS_DATA_ERROR';
export const HIDE_REQUEST_ENVIRONMENTS_ERROR = 'HIDE_REQUEST_ENVIRONMENTS_ERROR';
 
export const REQUEST_LOGS_DATA = 'REQUEST_LOGS_DATA';
export const RECEIVE_LOGS_DATA_SUCCESS = 'RECEIVE_LOGS_DATA_SUCCESS';
Loading
Loading
@@ -13,6 +18,7 @@ export const RECEIVE_LOGS_DATA_ERROR = 'RECEIVE_LOGS_DATA_ERROR';
export const REQUEST_LOGS_DATA_PREPEND = 'REQUEST_LOGS_DATA_PREPEND';
export const RECEIVE_LOGS_DATA_PREPEND_SUCCESS = 'RECEIVE_LOGS_DATA_PREPEND_SUCCESS';
export const RECEIVE_LOGS_DATA_PREPEND_ERROR = 'RECEIVE_LOGS_DATA_PREPEND_ERROR';
export const HIDE_REQUEST_LOGS_ERROR = 'HIDE_REQUEST_LOGS_ERROR';
 
export const RECEIVE_PODS_DATA_SUCCESS = 'RECEIVE_PODS_DATA_SUCCESS';
export const RECEIVE_PODS_DATA_ERROR = 'RECEIVE_PODS_DATA_ERROR';
Loading
Loading
@@ -18,6 +18,12 @@ export default {
state.timeRange.selected = timeRange;
state.timeRange.current = convertToFixedRange(timeRange);
},
[types.SHOW_TIME_RANGE_INVALID_WARNING](state) {
state.timeRange.invalidWarning = true;
},
[types.HIDE_TIME_RANGE_INVALID_WARNING](state) {
state.timeRange.invalidWarning = false;
},
 
// Environments Data
[types.SET_PROJECT_ENVIRONMENT](state, environmentName) {
Loading
Loading
@@ -38,6 +44,10 @@ export default {
[types.RECEIVE_ENVIRONMENTS_DATA_ERROR](state) {
state.environments.options = [];
state.environments.isLoading = false;
state.environments.fetchError = true;
},
[types.HIDE_REQUEST_ENVIRONMENTS_ERROR](state) {
state.environments.fetchError = false;
},
 
// Logs data
Loading
Loading
@@ -63,6 +73,7 @@ export default {
[types.RECEIVE_LOGS_DATA_ERROR](state) {
state.logs.lines = [];
state.logs.isLoading = false;
state.logs.fetchError = true;
},
 
[types.REQUEST_LOGS_DATA_PREPEND](state) {
Loading
Loading
@@ -80,6 +91,10 @@ export default {
},
[types.RECEIVE_LOGS_DATA_PREPEND_ERROR](state) {
state.logs.isLoading = false;
state.logs.fetchError = true;
},
[types.HIDE_REQUEST_LOGS_ERROR](state) {
state.logs.fetchError = false;
},
 
// Pods data
Loading
Loading
Loading
Loading
@@ -16,6 +16,8 @@ export default () => ({
selected: defaultTimeRange,
// Current time range, must be fixed
current: convertToFixedRange(defaultTimeRange),
invalidWarning: false,
},
 
/**
Loading
Loading
@@ -25,6 +27,7 @@ export default () => ({
options: [],
isLoading: false,
current: null,
fetchError: false,
},
 
/**
Loading
Loading
@@ -39,6 +42,8 @@ export default () => ({
*/
cursor: null,
isComplete: false,
fetchError: false,
},
 
/**
Loading
Loading
Loading
Loading
@@ -474,6 +474,9 @@ img.emoji {
.mw-70p { max-width: 70%; }
.mw-90p { max-width: 90%; }
 
// By default flex items don't shrink below their minimum content size.
// To change this, these clases set a min-width or min-height
.min-width-0 { min-width: 0; }
.min-height-0 { min-height: 0; }
 
.svg-w-100 {
Loading
Loading
Loading
Loading
@@ -199,8 +199,8 @@
/*
* Mixin that handles the container for the job logs (CI/CD and kubernetes pod logs)
*/
@mixin build-trace {
background: $black;
@mixin build-trace($background: $black) {
background: $background;
color: $gray-darkest;
white-space: pre;
overflow-x: auto;
Loading
Loading
@@ -243,7 +243,7 @@
/*
* Mixin that handles the position of the controls placed on the top bar
*/
@mixin build-controllers($control-font-size, $flex-direction, $with-grow, $flex-grow-size, $svg-display: 'block', $svg-top: '2px') {
@mixin build-controllers($control-font-size, $flex-direction, $with-grow, $flex-grow-size, $svg-display: block, $svg-top: 2px) {
display: flex;
font-size: $control-font-size;
justify-content: $flex-direction;
Loading
Loading
Loading
Loading
@@ -641,6 +641,14 @@ $issue-boards-breadcrumbs-height-xs: 63px;
$issue-board-list-difference-xs: $header-height + $issue-boards-breadcrumbs-height-xs;
$issue-board-list-difference-sm: $header-height + $breadcrumb-min-height;
$issue-board-list-difference-md: $issue-board-list-difference-sm + $issue-boards-filter-height;
/*
The following heights are used in environment_logs.scss and are used for calculation of the log viewer height.
*/
$environment-logs-breadcrumbs-height: 63px;
$environment-logs-breadcrumbs-height-md: $breadcrumb-min-height;
$environment-logs-difference-xs-up: $header-height + $environment-logs-breadcrumbs-height;
$environment-logs-difference-md-up: $header-height + $environment-logs-breadcrumbs-height-md;
 
/*
* Avatar
Loading
Loading
Loading
Loading
@@ -356,54 +356,3 @@
}
}
}
.environment-logs-viewer {
.build-trace-container {
position: relative;
}
.log-lines,
.gl-infinite-scroll-container {
// makes scrollbar visible by creating contrast
background: $black;
}
.gl-infinite-scroll-legend {
margin: 0;
}
.build-trace {
@include build-trace();
margin: 0;
}
.top-bar {
.date-time-picker-wrapper,
.dropdown-toggle {
@include media-breakpoint-up(md) {
width: 140px;
}
@include media-breakpoint-up(lg) {
width: 160px;
}
}
.controllers {
@include build-controllers(16px, flex-end, false, 2);
}
}
.btn-refresh svg {
top: 0;
}
.build-loader-animation {
@include build-loader-animation;
}
.log-footer {
color: $white-normal;
background-color: $gray-900;
}
}
.environment-logs-page {
.content-wrapper {
padding-bottom: 0;
}
}
.environment-logs-viewer {
height: calc(100vh - #{$environment-logs-difference-xs-up});
min-height: 700px;
@include media-breakpoint-up(md) {
height: calc(100vh - #{$environment-logs-difference-md-up});
}
.with-performance-bar & {
height: calc(100vh - #{$environment-logs-difference-xs-up} - #{$performance-bar-height});
@include media-breakpoint-up(md) {
height: calc(100vh - #{$environment-logs-difference-md-up} - #{$performance-bar-height});
}
}
.top-bar {
.date-time-picker-wrapper,
.dropdown-toggle {
@include media-breakpoint-up(md) {
width: 140px;
}
@include media-breakpoint-up(lg) {
width: 160px;
}
}
.controllers {
@include build-controllers(16px, flex-end, false, 2, inline);
}
}
.log-lines,
.gl-infinite-scroll-container {
// makes scrollbar visible by creating contrast
background: $black;
height: 100%;
}
.build-trace {
@include build-trace($black);
}
.gl-infinite-scroll-legend {
margin: 0;
}
.build-loader-animation {
@include build-loader-animation;
}
}
Loading
Loading
@@ -256,6 +256,7 @@ module ApplicationHelper
def page_class
class_names = []
class_names << 'issue-boards-page' if current_controller?(:boards)
class_names << 'environment-logs-page' if current_controller?(:logs)
class_names << 'with-performance-bar' if performance_bar_enabled?
class_names << system_message_class
class_names
Loading
Loading
---
title: Enable log explorer to use the full height of the screen
merge_request: 28312
author:
type: added
Loading
Loading
@@ -7898,6 +7898,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
 
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
msgstr ""
 
Loading
Loading
@@ -12811,9 +12814,6 @@ msgstr ""
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
 
msgid "Metrics|There was an error fetching the logs, please try again"
msgstr ""
msgid "Metrics|There was an error getting deployment information."
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -47,7 +47,7 @@ describe('EnvironmentLogs', () => {
const findLogControlButtons = () => wrapper.find({ name: 'log-control-buttons-stub' });
 
const findInfiniteScroll = () => wrapper.find({ ref: 'infiniteScroll' });
const findLogTrace = () => wrapper.find('.js-log-trace');
const findLogTrace = () => wrapper.find({ ref: 'logTrace' });
const findLogFooter = () => wrapper.find({ ref: 'logFooter' });
const getInfiniteScrollAttr = attr => parseInt(findInfiniteScroll().attributes(attr), 10);
 
Loading
Loading
@@ -169,16 +169,12 @@ describe('EnvironmentLogs', () => {
expect(updateControlBtnsMock).not.toHaveBeenCalled();
});
 
it('shows an infinite scroll with height and no content', () => {
expect(getInfiniteScrollAttr('max-list-height')).toBeGreaterThan(0);
it('shows an infinite scroll with no content', () => {
expect(getInfiniteScrollAttr('fetched-items')).toBe(0);
});
 
it('shows an infinite scroll container with equal height and max-height ', () => {
const height = getInfiniteScrollAttr('max-list-height');
expect(height).toEqual(expect.any(Number));
expect(findInfiniteScroll().attributes('style')).toMatch(`height: ${height}px;`);
it('shows an infinite scroll container with no set max-height ', () => {
expect(findInfiniteScroll().attributes('max-list-height')).toBeUndefined();
});
 
it('shows a logs trace', () => {
Loading
Loading
@@ -270,8 +266,7 @@ describe('EnvironmentLogs', () => {
expect(findAdvancedFilters().exists()).toBe(true);
});
 
it('shows infinite scroll with height and no content', () => {
expect(getInfiniteScrollAttr('max-list-height')).toBeGreaterThan(0);
it('shows infinite scroll with content', () => {
expect(getInfiniteScrollAttr('fetched-items')).toBe(mockTrace.length);
});
 
Loading
Loading
Loading
Loading
@@ -38,7 +38,7 @@ jest.mock('~/logs/utils');
 
const mockDefaultRange = {
start: '2020-01-10T18:00:00.000Z',
end: '2020-01-10T10:00:00.000Z',
end: '2020-01-10T19:00:00.000Z',
};
const mockFixedRange = {
start: '2020-01-09T18:06:20.000Z',
Loading
Loading
@@ -145,9 +145,6 @@ describe('Logs Store actions', () => {
{ type: types.RECEIVE_ENVIRONMENTS_DATA_ERROR },
],
[],
() => {
expect(flash).toHaveBeenCalledTimes(1);
},
);
});
});
Loading
Loading
@@ -186,6 +183,7 @@ describe('Logs Store actions', () => {
 
it('should commit logs and pod data when there is pod name defined', () => {
state.pods.current = mockPodName;
state.timeRange.current = mockFixedRange;
 
return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
expect(latestGetParams()).toMatchObject({
Loading
Loading
@@ -214,22 +212,26 @@ describe('Logs Store actions', () => {
state.search = mockSearch;
state.timeRange.current = 'INVALID_TIME_RANGE';
 
expectedMutations.splice(1, 0, {
type: types.SHOW_TIME_RANGE_INVALID_WARNING,
});
return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
expect(latestGetParams()).toEqual({
pod_name: mockPodName,
search: mockSearch,
});
// Warning about time ranges was issued
expect(flash).toHaveBeenCalledTimes(1);
expect(flash).toHaveBeenCalledWith(expect.any(String), 'warning');
});
});
 
it('should commit logs and pod data when no pod name defined', () => {
state.timeRange.current = mockDefaultRange;
state.timeRange.current = defaultTimeRange;
 
return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
expect(latestGetParams()).toEqual({});
expect(latestGetParams()).toEqual({
start_time: expect.any(String),
end_time: expect.any(String),
});
});
});
});
Loading
Loading
@@ -249,6 +251,7 @@ describe('Logs Store actions', () => {
 
it('should commit logs and pod data when there is pod name defined', () => {
state.pods.current = mockPodName;
state.timeRange.current = mockFixedRange;
 
expectedActions = [];
 
Loading
Loading
@@ -293,6 +296,10 @@ describe('Logs Store actions', () => {
state.search = mockSearch;
state.timeRange.current = 'INVALID_TIME_RANGE';
 
expectedMutations.splice(1, 0, {
type: types.SHOW_TIME_RANGE_INVALID_WARNING,
});
return testAction(
fetchMoreLogsPrepend,
null,
Loading
Loading
@@ -304,15 +311,12 @@ describe('Logs Store actions', () => {
pod_name: mockPodName,
search: mockSearch,
});
// Warning about time ranges was issued
expect(flash).toHaveBeenCalledTimes(1);
expect(flash).toHaveBeenCalledWith(expect.any(String), 'warning');
},
);
});
 
it('should commit logs and pod data when no pod name defined', () => {
state.timeRange.current = mockDefaultRange;
state.timeRange.current = defaultTimeRange;
 
return testAction(
fetchMoreLogsPrepend,
Loading
Loading
@@ -321,7 +325,10 @@ describe('Logs Store actions', () => {
expectedMutations,
expectedActions,
() => {
expect(latestGetParams()).toEqual({});
expect(latestGetParams()).toEqual({
start_time: expect.any(String),
end_time: expect.any(String),
});
},
);
});
Loading
Loading
@@ -357,6 +364,7 @@ describe('Logs Store actions', () => {
it('fetchLogs should commit logs and pod errors', () => {
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
state.timeRange.current = defaultTimeRange;
 
return testAction(
fetchLogs,
Loading
Loading
@@ -377,6 +385,7 @@ describe('Logs Store actions', () => {
it('fetchMoreLogsPrepend should commit logs and pod errors', () => {
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
state.timeRange.current = defaultTimeRange;
 
return testAction(
fetchMoreLogsPrepend,
Loading
Loading
Loading
Loading
@@ -67,6 +67,7 @@ describe('Logs Store Mutations', () => {
options: [],
isLoading: false,
current: null,
fetchError: true,
});
});
});
Loading
Loading
@@ -83,6 +84,7 @@ describe('Logs Store Mutations', () => {
expect(state.logs).toEqual({
lines: [],
cursor: null,
fetchError: false,
isLoading: true,
isComplete: false,
});
Loading
Loading
@@ -101,6 +103,7 @@ describe('Logs Store Mutations', () => {
isLoading: false,
cursor: mockCursor,
isComplete: false,
fetchError: false,
});
});
 
Loading
Loading
@@ -115,6 +118,7 @@ describe('Logs Store Mutations', () => {
isLoading: false,
cursor: null,
isComplete: true,
fetchError: false,
});
});
});
Loading
Loading
@@ -128,6 +132,7 @@ describe('Logs Store Mutations', () => {
isLoading: false,
cursor: null,
isComplete: false,
fetchError: true,
});
});
});
Loading
Loading
@@ -152,6 +157,7 @@ describe('Logs Store Mutations', () => {
isLoading: false,
cursor: mockCursor,
isComplete: false,
fetchError: false,
});
});
 
Loading
Loading
@@ -171,6 +177,7 @@ describe('Logs Store Mutations', () => {
isLoading: false,
cursor: mockNextCursor,
isComplete: false,
fetchError: false,
});
});
 
Loading
Loading
@@ -185,6 +192,7 @@ describe('Logs Store Mutations', () => {
isLoading: false,
cursor: null,
isComplete: true,
fetchError: false,
});
});
});
Loading
Loading
@@ -194,6 +202,7 @@ describe('Logs Store Mutations', () => {
mutations[types.RECEIVE_LOGS_DATA_PREPEND_ERROR](state);
 
expect(state.logs.isLoading).toBe(false);
expect(state.logs.fetchError).toBe(true);
});
});
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment