前言
McLogs-Next是什么?
McLogs-Next-API 是一个由梦泽主导开发的现代化 Minecraft 日志分析与分享平台,基于业界成熟的 @aternos/mclogs 底层架构进行深度重构与增强。该项目诞生于 Minecraft 服务器运维的实际需求——面对动辄数千行的复杂日志,传统的手动排查方式往往需要 15-30 分钟才能定位问题,而 McLogs-Next-API 通过智能化技术将这一过程缩短至秒级。
本软件专注于解决三大核心痛点:快速错误定位、智能日志分析与便捷日志分享。相较于传统的 @aternos/mclogs 方案,McLogs-Next-API 在完全兼容其所有基础功能——包括语法高亮显示、行号标记、多存储后端支持(MongoDB、Redis、文件系统)——的同时,引入了AI 驱动的深度日志分析引擎,能够自动识别内存溢出、插件冲突、配置错误等常见故障模式,并提供针对性的修复建议。
项目采用无头 API(Headless API)架构设计,仅通过 RESTful API 接口与自动化文档(APIDocs)对外提供服务,彻底解耦前端展示与后端逻辑。这种设计带来了显著的工程优势。
为什么是Docker Compose?
我们使用Docker Compose设计这款软件也是看中了Docker Compose的便捷性:
NingZeStudio/McLogs-Next-API 采用现代化的技术栈构建,其核心架构包含多个关键组件:MongoDB 作为数据库存储支持百万级日志秒级检索,Redis 提供内存加速以提升API性能,同时还集成了大模型进行智能日志分析。这种多服务架构天然需要容器编排工具来协调各个组件的启动顺序、网络通信和数据共享。Docker Compose 通过单一的 docker-compose.yml 文件定义这些服务之间的依赖关系,确保数据库、缓存、前端和后端API能够按照正确的顺序启动并自动连接,避免了手动配置每个容器网络端口的繁琐过程,实现了"一键部署完整环境"的便捷性。
搭建后端Headless API
准备环境
在正式开始之前,你需要一台可以公网访问的服务器,如果你的运维能力不够强的话这边推荐安装一个面板,这里推荐宝塔面板,本教程也会基于宝塔面板和宝塔Docker模块展开。宝塔官网:https://bt.cn/,运行如下命令安装宝塔:
if [ -f /usr/bin/curl ];then curl -sSO https://download.bt.cn/install/install_panel.sh;else wget -O install_panel.sh https://download.bt.cn/install/install_panel.sh;fi;bash install_panel.sh ed8484bec安装完成之后你就会看到类似如下反馈:
通过外网IPv4地址、访问宝塔面板,再输入用户名和密码登录之后,绑定宝塔账号就可以开始用了。
开始搭建
接着就是下载程序本身,前往Github下载:https://github.com/NingZeStudio/McLogs-Next-API
下载好压缩包之后,访问宝塔面板,选择LNMP环境安装,完成之后再点击侧栏的文件管理,按照图片上的路径,依次点击www>wwwroot,来到wwwroot目录,在这里新建一个LogShare目录,当然这个名字随便,进入这个目录之后,按照图片点击上传按钮上传刚刚下载的压缩包:
上传完成解压之后,在文件右侧更多按钮选择解压,解压完成之后,顶部工具栏选择终端,运行如下命令安装Composer依赖:
# 如果没有安装Composer
apt install composer # Ubuntu / Debian
yum install composer # RedHat / CentOS
# 安装依赖
composer install --ignore-platform-reqs修改配置文件
解压完成之后,进入www/wwwroot/你的文件夹名字/docker文件夹,找到并打开mclogs.conf配置文件,内容如下:
server {
listen 9300;
sendfile off;
server_name api.mclogs.lemwood.icu mc.logshare.cn;
root /web/mclogs/api/public;
error_log /var/log/nginx/api-mclogs-error.log error;
client_max_body_size 210m;
location / {
try_files $uri /index.php;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
include fastcgi_params;
}
}需要修改的部分是server_name的值,改成你的API地址即可,就比如我的API地址是api.logshare.cn,我就写:
server {
# nore...
server_name api.logshare.cn;
# more...
}接着进入www/wwwroot/你的文件夹名字/core/config/文件夹,里面是需要修改的配置文件,其实只需要修改urls.php和ai.php,这是urls.php文件代码:
<?php
$config = [
/**
* The base URL for the front end
*
* Should not end with a slash
*/
'baseUrl' => 'https://mclogs.lemwood.icu',
/**
* The base URL for the API
*
* Should not end with a slash
*/
'apiBaseUrl' => 'https://api.mclogs.lemwood.icu',
];这个配置文件配的的是API返回的地址和API地址,把baseUrl改成你的项目前端地址,后面会教你怎么部署前端,apiBaseUrl改成你在/docker/mclogs.conf中设置的域名。
然后再打开ai.php文件,这个文件是配置那个AI分析接口的,主要是配置apikey和模型,我们使用的是Google得生成式模型接口,也就是Google AiStudio的Gemini,先去这里获取APIKey:https://aistudio.google.com/app/apikey,是AIza...开头的那个,然后这里给出ai.php的完整代码:
<?php
return [
'gemini_api_key' => '这里写你申请到的APIKey',
'model' => '这里写你的模型'
];拉取和构建镜像
由于宝塔面板自带的容器编排存在一些问题:_当我使用新建容器编排的时候选择我的Docker Cmpose文件,它并不支持Yaml中的Build配置,无法触发构建镜像,且拉取镜像之后无法自动建立容器,疑似兼容性欠佳_;所以我们需要使用文件管理自带的终端使用Docker Compose命令来部署Compose项目,之后依旧可以使用宝塔Docker模块来管理。
点击侧栏的Docker,安装宝塔Docker模块,之后来到www/wwwroot/你刚刚创建的文件夹/docker目录,同样点击终端按钮,运行如下命令拉取容器编排:
docker-compose up -d如果有问题的话你会成功看到如下效果:

如果你的截图和我类似,那么恭喜你,完成了80%,接下来在宝塔侧栏点击安全,放行9300端口,这个时候你就可以在内网访问api端点了,接下来开始反向代理。
反向代理
虽然现在的API已经可以在内网访问了,但是前端没办法通过内网访问你的API呀!还知道最开始我叫你们安装LNMP环境了吗?这个时候Nginx就派上用场了,按照图片来,找到侧边栏的Docker,点击并进入,顶部TAB栏选择网站,点进去之后,再点击创建按钮。
接着,我们点击新建按钮,然后按照图片选择反代容器,填写好你刚刚在配置文件中写的域名之后,反代容器选择mclogs-nginx,端口会自动获取,不用管他,确认即可。
然后去你的域名服务商,解析一下域名:
解析完成之后,就该申请SSL了,如图所示:

好的那么到了这里,整个McLogs-Next-API的部署也是完成了,可以通过你的域名访问API了。
部署官方Web UI实现
我们给McLogs-Next Headless API使用Vue3开发了一套Web UI实现,使用的技术栈是Vue3、TypeScript和TailwindCSS,仓库地址是:https://github.com/NingZeStudio/McLogs-Next-UI,这个前端部署起来很简单。
修改配置和构建
先克隆仓库到本地,运行如下命令:
git clone https://github.com/NingZeStudio/McLogs-Next-UI.git克隆仓库之后,使用npm install安装所需要的依赖,和其他项目一样,使用npm run dev开启开发服务器。
npm是Node.js官方开发的一个JavaScript包管理器,使用npm之前你需要先安装nodejs,前往官网下载:https://nodejs.org。
Linux可以执行如下命令安装nvm和nodejs以及npm# 下载并安装 nvm: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash # 代替重启 shell \. "$HOME/.nvm/nvm.sh" # 下载并安装 Node.js: nvm install 24 # 验证 Node.js 版本: node -v # Should print "v24.13.1". # 验证 npm 版本: npm -v # Should print "11.8.0".Windows使用如下指令安装:
# 下载并安装 Chocolatey: powershell -c "irm https://community.chocolatey.org/install.ps1|iex" # 下载并安装 Node.js: choco install nodejs --version="24.13.1" # 验证 Node.js 版本: node -v # Should print "v24.13.1". # 验证 npm 版本: npm -v # Should print "11.8.0".
我们需要编辑的文件并不多,主要就是几个TypeScript文件,分别是sharedUtils.ts和api.ts,首先是api.ts:
import axios from 'axios'
const baseURL = 'https://api.logshare.cn'
export const apiClient = axios.create({
baseURL: baseURL
})
export const getApiUrl = (endpoint: string) => {
const cleanEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint
return `${baseURL}/${cleanEndpoint}`
}我们需要做的是把const baseURL = 'https://api.logshare.cn'中的https://api.logshare.cn改成你的后端域名,保存后打开sharedUtils.ts:
// Shared utilities for both ApiDocsView and LogView components
// Common constants
export const API_BASE_URL = 'https://api.logshare.cn/'
export const FRONTEND_BASE_URL = 'https://logshare.cn/'
// Common API endpoints
export const API_ENDPOINTS = {
LOG: '/1/log',
ANALYSE: '/1/analyse',
INSIGHTS: '/1/insights/',
RAW: '/1/raw/',
AI_ANALYSIS: '/1/ai-analysis/',
LIMITS: '/1/limits',
DELETE: '/1/delete/',
RATE_ERROR: '/1/errors/rate'
}
// Common HTTP methods
export const HTTP_METHODS = {
GET: 'GET',
POST: 'POST',
PUT: 'PUT',
DELETE: 'DELETE',
PATCH: 'PATCH'
}
// Common response types
export interface ApiResponse {
success: boolean
error?: string
}
// Common log levels
export const LOG_LEVELS = {
ERROR: 'error',
WARNING: 'warning',
INFO: 'info',
DEBUG: 'debug',
CRITICAL: 'critical',
EMERGENCY: 'emergency'
}
// Utility function to get API URL
export const getApiUrl = (endpoint: string): string => {
return `${API_BASE_URL}${endpoint}`
}
// Utility function to get frontend URL
export const getFrontendUrl = (path: string): string => {
return `${FRONTEND_BASE_URL}${path}`
}
// Utility function to validate log ID format
export const isValidLogId = (id: string): boolean => {
// Assuming log IDs are alphanumeric with certain length
const logIdRegex = /^[a-zA-Z0-9-_]+$/;
return logIdRegex.test(id) && id.length >= 3 && id.length <= 50
}
// Utility function to format bytes to human readable format
export const formatBytes = (bytes: number, decimals: number = 2): string => {
if (bytes === 0) return '0 Bytes'
const k = 1024
const dm = decimals < 0 ? 0 : decimals
const sizes = ['Bytes', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
}
// Utility function to truncate text
export const truncateText = (text: string, maxLength: number, suffix: string = '...'): string => {
if (text.length <= maxLength) return text
return text.substring(0, maxLength) + suffix
}
// Utility function to debounce function calls
export const debounce = <T extends (...args: any[]) => any>(
func: T,
delay: number
): ((...args: Parameters<T>) => void) => {
let timeoutId: ReturnType<typeof setTimeout> | null = null
return (...args: Parameters<T>): void => {
if (timeoutId) clearTimeout(timeoutId)
timeoutId = setTimeout(() => func(...args), delay)
}
}
// Utility function to throttle function calls
export const throttle = <T extends (...args: any[]) => any>(
func: T,
limit: number
): ((...args: Parameters<T>) => void) => {
let inThrottle: boolean
return (...args: Parameters<T>): void => {
if (!inThrottle) {
func(...args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
// Utility function to copy text to clipboard
export const copyToClipboard = async (text: string): Promise<boolean> => {
try {
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(text)
return true
} else {
// Fallback for older browsers or insecure contexts
const textArea = document.createElement('textarea')
textArea.value = text
document.body.appendChild(textArea)
textArea.focus()
textArea.select()
const successful = document.execCommand('copy')
document.body.removeChild(textArea)
return successful
}
} catch (error) {
console.error('Failed to copy text to clipboard:', error)
return false
}
}
// Utility function to download content as file
export const downloadFile = (content: string, filename: string, contentType: string = 'text/plain'): void => {
const blob = new Blob([content], { type: contentType })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = filename
link.style.display = 'none'
document.body.appendChild(link)
link.click()
// Clean up
document.body.removeChild(link)
URL.revokeObjectURL(url)
}
// Utility function to detect if running in a mobile environment
export const isMobile = (): boolean => {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
}
// Utility function to get current timestamp
export const getCurrentTimestamp = (): number => {
return Math.floor(Date.now() / 1000)
}
// Utility function to format timestamp to readable date
export const formatDate = (timestamp: number): string => {
const date = new Date(timestamp * 1000)
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString()
}我们需要修改的地方是这两块,也是改成自己的域名和自己的后端域名:
export const API_BASE_URL = 'https://api.logshare.cn/'
export const FRONTEND_BASE_URL = 'https://logshare.cn/'在这两个配置文件修改完成之后,你可以尝试修改前端代码来实现自己的想法,接着运行:
npm run build # 开始构建构建完成之后,构建产物在dist目录,把这个目录的文件全部上传到服务器之后,新建一个静态网站就大功告成了!
新建静态网站
重新打开宝塔面板,侧栏找到网站点击并进入,TAB栏选择HTML项目,然后点击添加项目,在新弹出的弹窗里面,输入绑定域名也就是你准备的前端域名,根目录可以默认,点击确定,注意这个根目录要记住,后面要用。

然后在在侧栏点击文件管理,进入刚刚网站的根目录,接着在本地把刚刚的dist目录打包成压缩包,上传到根目录之后,再按照上面的方法申请SSL之后,网站就可以正常访问了。